diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index 14b7673caf..bc9cfba6ef 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -746,7 +746,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState* p) sw->addCase(LLConstantInt::get(llvm::IntegerType::get(gIR->context(), 32), it->second), casebb); p->scope() = IRScope(casebb); - DtoGoto(stmt->loc, it->first, stmt->enclosingFinally); + DtoGoto(stmt->loc, it->first); } p->scope() = IRScope(bb); diff --git a/gen/functions.cpp b/gen/functions.cpp index e9803199c7..313c1aa0b3 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -884,40 +884,41 @@ void DtoDefineFunction(FuncDeclaration* fd) } } - FuncGen fg; - irFunc->gen = &fg; - - DtoCreateNestedContext(fd); - - if (fd->vresult && ! - fd->vresult->nestedrefs.dim // FIXME: not sure here :/ - ) { - DtoVarDeclaration(fd->vresult); + ScopeStack scopeStack(gIR); + irFunc->scopes = &scopeStack; + + DtoCreateNestedContext(fd); + + if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/ + { + DtoVarDeclaration(fd->vresult); + } + + // D varargs: prepare _argptr and _arguments + if (f->linkage == LINKd && f->varargs == 1) + { + // allocate _argptr (of type core.stdc.stdarg.va_list) + LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); + irFunc->_argptr = argptrmem; + + // initialize _argptr with a call to the va_start intrinsic + LLValue* vaStartArg = gABI->prepareVaStart(argptrmem); + llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb()); + + // copy _arguments to a memory location + LLType* argumentsType = irFunc->_arguments->getType(); + LLValue* argumentsmem = DtoRawAlloca(argumentsType, 0, "_arguments_mem"); + new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb()); + irFunc->_arguments = argumentsmem; + } + + // output function body + Statement_toIR(fd->fbody, gIR); + + irFunc->scopes = 0; } - // D varargs: prepare _argptr and _arguments - if (f->linkage == LINKd && f->varargs == 1) - { - // allocate _argptr (of type core.stdc.stdarg.va_list) - LLValue* argptrmem = DtoAlloca(Type::tvalist, "_argptr_mem"); - irFunc->_argptr = argptrmem; - - // initialize _argptr with a call to the va_start intrinsic - LLValue* vaStartArg = gABI->prepareVaStart(argptrmem); - llvm::CallInst::Create(GET_INTRINSIC_DECL(vastart), vaStartArg, "", gIR->scopebb()); - - // copy _arguments to a memory location - LLType* argumentsType = irFunc->_arguments->getType(); - LLValue* argumentsmem = DtoRawAlloca(argumentsType, 0, "_arguments_mem"); - new llvm::StoreInst(irFunc->_arguments, argumentsmem, gIR->scopebb()); - irFunc->_arguments = argumentsmem; - } - - // output function body - codegenFunction(fd->fbody, gIR); - irFunc->gen = 0; - llvm::BasicBlock* bb = gIR->scopebb(); if (pred_begin(bb) == pred_end(bb) && bb != &bb->getParent()->getEntryBlock()) { // This block is trivially unreachable, so just delete it. diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 0cf8688cc4..24022731e1 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -41,23 +41,6 @@ const IRScope& IRScope::operator=(const IRScope& rhs) return *this; } -////////////////////////////////////////////////////////////////////////////////////////// - -IRTargetScope::IRTargetScope( - Statement* s, - EnclosingTryFinally* enclosinghandler, - llvm::BasicBlock* continueTarget, - llvm::BasicBlock* breakTarget, - bool onlyLabeledBreak -) -{ - this->s = s; - this->enclosinghandler = enclosinghandler; - this->breakTarget = breakTarget; - this->continueTarget = continueTarget; - this->onlyLabeledBreak = onlyLabeledBreak; -} - ////////////////////////////////////////////////////////////////////////////////////////// IRState::IRState(const char *name, llvm::LLVMContext &context) : module(name, context), DBuilder(this) diff --git a/gen/irstate.h b/gen/irstate.h index 79ada43db0..09f38bde76 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -201,20 +201,20 @@ struct IRState template llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const char* Name) { - FuncGen& funcGen = *func()->gen; + //ScopeStack& funcGen = *func()->scopes; LLFunction* fn = llvm::dyn_cast(Callee); - const bool hasTemporaries = funcGen.hasTemporariesToDestruct(); + /*const bool hasTemporaries = funcGen.hasTemporariesToDestruct(); // intrinsics don't support invoking and 'nounwind' functions don't need it. const bool doesNotThrow = (fn && (fn->isIntrinsic() || fn->doesNotThrow())); if (doesNotThrow || (!hasTemporaries && funcGen.landingPad == NULL)) - { + {*/ llvm::CallInst* call = ir->CreateCall(Callee, args, Name); if (fn) call->setAttributes(fn->getAttributes()); return call; - } + /*} if (hasTemporaries) funcGen.prepareToDestructAllTemporariesOnThrow(this); @@ -229,10 +229,9 @@ llvm::CallSite IRState::CreateCallOrInvoke(LLValue* Callee, const T &args, const funcGen.landingPadInfo.pop(); scope() = IRScope(postinvoke); - return invoke; + return invoke;*/ } -void codegenFunction(Statement *s, IRState *irs); void Statement_toIR(Statement *s, IRState *irs); #endif // LDC_GEN_IRSTATE_H diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 6c87cc462b..237b90d957 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -227,7 +227,7 @@ LLValue *DtoModuleFileName(Module* M, const Loc& loc) /*//////////////////////////////////////////////////////////////////////////////////////// // GOTO HELPER ////////////////////////////////////////////////////////////////////////////////////////*/ -void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally) +void DtoGoto(Loc &loc, LabelDsymbol *target) { assert(!gIR->scopereturned()); @@ -238,90 +238,9 @@ void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally) fatal(); } - // find target basic block - std::string labelname = gIR->func()->gen->getScopedLabelName(target->ident->toChars()); - llvm::BasicBlock* &targetBB = gIR->func()->gen->labelToBB[labelname]; - if (targetBB == NULL) - targetBB = llvm::BasicBlock::Create(gIR->context(), "label_" + labelname, gIR->topfunc()); - - // emit code for finallys between goto and label - DtoEnclosingHandlers(loc, lblstmt); - - // goto into finally blocks is forbidden by the spec - // but should work fine - if (lblstmt->tf != sourceFinally) - { - error(loc, "spec disallows goto into or out of finally block"); - fatal(); - } - - llvm::BranchInst::Create(targetBB, gIR->scopebb()); + gIR->func()->scopes->jumpToLabel(loc, target->ident); } -/****************************************************************************************/ -/*//////////////////////////////////////////////////////////////////////////////////////// -// TRY-FINALLY -////////////////////////////////////////////////////////////////////////////////////////*/ - -void EnclosingTryFinally::emitCode(IRState * p) -{ - if (tf->finalbody) - { - llvm::BasicBlock* oldpad = p->func()->gen->landingPad; - p->func()->gen->landingPad = landingPad; - Statement_toIR(tf->finalbody, p); - p->func()->gen->landingPad = oldpad; - } -} - -//////////////////////////////////////////////////////////////////////////////////////// - -void DtoEnclosingHandlers(Loc& loc, Statement* target) -{ - // labels are a special case: they are not required to enclose the current scope - // for them we use the enclosing scope handler as a reference point - LabelStatement* lblstmt = target ? target->isLabelStatement() : 0; - if (lblstmt) - target = lblstmt->enclosingScopeExit; - - // Figure out up until what handler we need to emit. Note that we need to - // use use indices instead of iterators in the loop where we actually emit - // them, as emitCode() might itself push/pop from the vector if it contains - // control flow and and thus invalidate the latter. - FuncGen::TargetScopeVec& scopes = gIR->func()->gen->targetScopes; - size_t remainingScopes = scopes.size(); - while (remainingScopes != 0) { - if (scopes[remainingScopes - 1].s == target) { - break; - } - --remainingScopes; - } - - if (target && !remainingScopes) { - if (lblstmt) - error(loc, "cannot goto into try, volatile or synchronized statement at %s", target->loc.toChars()); - else - error(loc, "internal error, cannot find jump path to statement at %s", target->loc.toChars()); - return; - } - - // - // emit code for enclosing handlers - // - - // since the labelstatements possibly inside are private - // and might already exist push a label scope - gIR->func()->gen->pushUniqueLabelScope("enclosing"); - - for (size_t i = scopes.size(); i > remainingScopes; --i) { - EnclosingTryFinally *tf = scopes[i - 1].enclosinghandler; - if (tf) tf->emitCode(gIR); - } - - gIR->func()->gen->popLabelScope(); -} - -/****************************************************************************************/ /*//////////////////////////////////////////////////////////////////////////////////////// // ASSIGNMENT HELPER (store this in that) ////////////////////////////////////////////////////////////////////////////////////////*/ diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index aaf5371771..32f7b69e45 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -22,16 +22,6 @@ #include "gen/llvm.h" #include "ir/irfuncty.h" -// this is used for tracking try-finally scopes -struct EnclosingTryFinally -{ - TryFinallyStatement* tf; - llvm::BasicBlock* landingPad; - void emitCode(IRState* p); - EnclosingTryFinally(TryFinallyStatement* _tf, llvm::BasicBlock* _pad) - : tf(_tf), landingPad(_pad) {} -}; - // dynamic memory helpers LLValue* DtoNew(Loc& loc, Type* newtype); LLValue* DtoNewStruct(Loc& loc, TypeStruct* newtype); @@ -54,12 +44,7 @@ void DtoAssert(Module* M, Loc& loc, DValue* msg); LLValue* DtoModuleFileName(Module* M, const Loc& loc); /// emits goto to LabelStatement with the target identifier -/// the sourceFinally is only used for error checking -void DtoGoto(Loc &loc, LabelDsymbol *target, TryFinallyStatement *sourceFinally); - -// Generates IR for enclosing handlers between the current state and -// the scope created by the 'target' statement. -void DtoEnclosingHandlers(Loc& loc, Statement* target); +void DtoGoto(Loc &loc, LabelDsymbol *target); /// Enters a critical section. void DtoEnterCritical(Loc& loc, LLValue* g); diff --git a/gen/statements.cpp b/gen/statements.cpp index 2bda71323b..379c70dd93 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -14,6 +14,7 @@ #include "port.h" #include "gen/abi.h" #include "gen/arrays.h" +#include "gen/classes.h" #include "gen/coverage.h" #include "gen/dvalue.h" #include "gen/irstate.h" @@ -23,7 +24,6 @@ #include "gen/runtime.h" #include "gen/tollvm.h" #include "ir/irfunction.h" -#include "ir/irlandingpad.h" #include "ir/irmodule.h" #if LDC_LLVM_VER >= 305 #include "llvm/IR/CFG.h" @@ -103,239 +103,8 @@ static LLValue* call_string_switch_runtime(llvm::Value* table, Expression* e) return call.getInstruction(); } - ////////////////////////////////////////////////////////////////////////////// -/* A visitor to walk entire tree of statements. - */ -class StatementVisitor : public Visitor -{ - void visitStmt(Statement *s) { s->accept(this); } -public: - // Import all functions from class Visitor - using Visitor::visit; - - void visit(ErrorStatement *s) { } - void visit(PeelStatement *s) - { - if (s->s) - visitStmt(s->s); - } - void visit(ExpStatement *s) { } - void visit(DtorExpStatement *s) { } - void visit(CompileStatement *s) { } - void visit(CompoundStatement *s) - { - if (s->statements && s->statements->dim) - { - for (size_t i = 0; i < s->statements->dim; i++) - { - if ((*s->statements)[i]) - visitStmt((*s->statements)[i]); - } - } - } - void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); } - void visit(UnrolledLoopStatement *s) - { - if (s->statements && s->statements->dim) - { - for (size_t i = 0; i < s->statements->dim; i++) - { - if ((*s->statements)[i]) - visitStmt((*s->statements)[i]); - } - } - } - void visit(ScopeStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(WhileStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(DoStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(ForStatement *s) - { - if (s->init) - visitStmt(s->init); - if (s->body) - visitStmt(s->body); - } - void visit(ForeachStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(ForeachRangeStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(IfStatement *s) - { - if (s->ifbody) - visitStmt(s->ifbody); - if (s->elsebody) - visitStmt(s->elsebody); - } - void visit(ConditionalStatement *s) { } - void visit(PragmaStatement *s) { } - void visit(StaticAssertStatement *s) { } - void visit(SwitchStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(CaseStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(CaseRangeStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(DefaultStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(GotoDefaultStatement *s) { } - void visit(GotoCaseStatement *s) { } - void visit(SwitchErrorStatement *s) { } - void visit(ReturnStatement *s) { } - void visit(BreakStatement *s) { } - void visit(ContinueStatement *s) { } - void visit(SynchronizedStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(WithStatement *s) - { - if (s->body) - visitStmt(s->body); - } - void visit(TryCatchStatement *s) - { - if (s->body) - visitStmt(s->body); - if (s->catches && s->catches->dim) - { - for (size_t i = 0; i < s->catches->dim; i++) - { - Catch *c = (*s->catches)[i]; - if (c && c->handler) - visitStmt(c->handler); - } - } - } - void visit(TryFinallyStatement *s) - { - if (s->body) - visitStmt(s->body); - if (s->finalbody) - visitStmt(s->finalbody); - } - void visit(OnScopeStatement *s) { } - void visit(ThrowStatement *s) { } - void visit(DebugStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(GotoStatement *s) { } - void visit(LabelStatement *s) - { - if (s->statement) - visitStmt(s->statement); - } - void visit(AsmStatement *s) { } - void visit(ImportStatement *s) { } -}; - -////////////////////////////////////////////////////////////////////////////// - -class FindEnclosingTryFinally : public StatementVisitor { - std::stack m_tryFinally; - std::stack m_switches; -public: - // Import all functions from class StatementVisitor - using StatementVisitor::visit; - - TryFinallyStatement *enclosingTryFinally() const - { - return m_tryFinally.empty() ? 0 : m_tryFinally.top(); - } - - SwitchStatement *enclosingSwitch() const - { - return m_switches.empty() ? 0 : m_switches.top(); - } - - void visit(SwitchStatement *s) - { - m_switches.push(s); - s->enclosingScopeExit = enclosingTryFinally(); - StatementVisitor::visit(s); - m_switches.pop(); - } - - void visit(CaseStatement *s) - { - s->enclosingScopeExit = enclosingTryFinally(); - if (s->enclosingScopeExit != enclosingSwitch()->enclosingScopeExit) - s->error("switch and case are in different try blocks"); - StatementVisitor::visit(s); - } - - void visit(DefaultStatement *s) - { - s->enclosingScopeExit = enclosingTryFinally(); - if (s->enclosingScopeExit != enclosingSwitch()->enclosingScopeExit) - s->error("switch and default case are in different try blocks"); - StatementVisitor::visit(s); - } - - void visit(TryFinallyStatement *s) - { - m_tryFinally.push(s); - s->body->accept(this); - m_tryFinally.pop(); - s->finalbody->accept(this); - } - - void visit(LabelStatement *s) - { - s->enclosingScopeExit = enclosingTryFinally(); - StatementVisitor::visit(s); - } - - void visit(GotoStatement *s) - { - s->enclosingScopeExit = enclosingTryFinally(); - StatementVisitor::visit(s); - } - - void visit(CompoundAsmStatement *s) - { - s->enclosingScopeExit = enclosingTryFinally(); - StatementVisitor::visit(s); - } -}; - -////////////////////////////////////////////////////////////////////////////// - - class ToIRVisitor : public Visitor { IRState *irs; public: @@ -371,16 +140,19 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); + // The LLVM value to return, or null for void returns. + llvm::Value *returnValue = 0; + // is there a return value expression? if (stmt->exp || (!stmt->exp && (irs->topfunc() == irs->mainFunc)) ) { // if the functions return type is void this means that // we are returning through a pointer argument - if (irs->topfunc()->getReturnType() == LLType::getVoidTy(gIR->context())) + if (irs->topfunc()->getReturnType() == LLType::getVoidTy(irs->context())) { // sanity check IrFunction* f = irs->func(); @@ -400,19 +172,12 @@ public: // call postblit if necessary if (!irs->func()->type->isref && !(f->decl->nrvo_can && f->decl->nrvo_var)) callPostblit(stmt->loc, stmt->exp, rvar->getLVal()); - - // emit scopes - DtoEnclosingHandlers(stmt->loc, NULL); - - // emit ret - gIR->ir->CreateRetVoid(); } // the return type is not void, so this is a normal "register" return else { - LLValue* v = 0; if (!stmt->exp && (irs->topfunc() == irs->mainFunc)) { - v = LLConstant::getNullValue(irs->mainFunc->getReturnType()); + returnValue = LLConstant::getNullValue(irs->mainFunc->getReturnType()); } else { if (stmt->exp->op == TOKnull) stmt->exp->type = irs->func()->type->next; @@ -426,10 +191,10 @@ public: dval = toElemDtor(ae); } // do abi specific transformations on the return value - v = getIrFunc(irs->func()->decl)->irFty.putRet(stmt->exp->type, dval); + returnValue = getIrFunc(irs->func()->decl)->irFty.putRet(stmt->exp->type, dval); } - IF_LOG Logger::cout() << "return value is '" <<*v << "'\n"; + IF_LOG Logger::cout() << "return value is '" << returnValue << "'\n"; IrFunction* f = irs->func(); // Hack around LDC assuming structs and static arrays are in memory: @@ -437,17 +202,17 @@ public: // value is a pointer to a struct or a static array, load from it // before returning. int ty = f->type->next->toBasetype()->ty; - if (v->getType() != irs->topfunc()->getReturnType() && + if (returnValue->getType() != irs->topfunc()->getReturnType() && (ty == Tstruct || ty == Tsarray - ) && isaPointer(v->getType())) + ) && isaPointer(returnValue->getType())) { Logger::println("Loading value for return"); - v = DtoLoad(v); + returnValue = DtoLoad(returnValue); } // can happen for classes and void main - if (v->getType() != irs->topfunc()->getReturnType()) + if (returnValue->getType() != irs->topfunc()->getReturnType()) { // for the main function this only happens if it is declared as void // and then contains a return (exp); statement. Since the actual @@ -455,34 +220,70 @@ public: // and return 0 instead // if we're not in main, just bitcast if (irs->topfunc() == irs->mainFunc) - v = LLConstant::getNullValue(irs->mainFunc->getReturnType()); + returnValue = LLConstant::getNullValue(irs->mainFunc->getReturnType()); else - v = gIR->ir->CreateBitCast(v, irs->topfunc()->getReturnType()); + returnValue = irs->ir->CreateBitCast(returnValue, + irs->topfunc()->getReturnType()); - IF_LOG Logger::cout() << "return value after cast: " << *v << '\n'; + IF_LOG Logger::cout() << "return value after cast: " << *returnValue << '\n'; } - - // emit scopes - DtoEnclosingHandlers(stmt->loc, NULL); - - // Hack: the frontend generates 'return 0;' as last statement of - // 'void main()'. But the debug location is missing. Use the end - // of function as debug location. - if (f->decl->isMain() && !stmt->loc.linnum) - gIR->DBuilder.EmitStopPoint(f->decl->endloc); - - gIR->ir->CreateRet(v); } } - // no return value expression means it's a void function else { - assert(irs->topfunc()->getReturnType() == LLType::getVoidTy(gIR->context())); - DtoEnclosingHandlers(stmt->loc, NULL); - gIR->ir->CreateRetVoid(); + // no return value expression means it's a void function. + assert(irs->topfunc()->getReturnType() == LLType::getVoidTy(irs->context())); } - // the return terminated this basicblock, start a new one + // If there are no cleanups to run, we try to keep the IR simple and + // just directly emit the return instruction. + + const bool loadFromSlot = irs->func()->scopes->currentCleanupScope() != 0; + if (loadFromSlot) { + const bool retBlockExisted = !!irs->func()->retBlock; + if (!retBlockExisted) { + irs->func()->retBlock = llvm::BasicBlock::Create( + irs->context(), "return", irs->topfunc()); + if (returnValue) { + irs->func()->retValSlot = DtoRawAlloca(returnValue->getType(), + 0, "return.slot"); + } + } + + // Create the store to the slot at the end of our current basic + // block, before we run the cleanups. + if (returnValue) { + irs->ir->CreateStore(returnValue, irs->func()->retValSlot); + } + + // Now run the cleanups. + irs->func()->scopes->runAllCleanups(irs->func()->retBlock); + + // If the return block already exists, we are golden. Otherwise, go + // ahead and emit it now. + if (retBlockExisted) { + return; + } + + irs->scope() = IRScope(irs->func()->retBlock); + } + + if (returnValue) { + // Hack: the frontend generates 'return 0;' as last statement of + // 'void main()'. But the debug location is missing. Use the end + // of function as debug location. + if (irs->func()->decl->isMain() && !stmt->loc.linnum) + irs->DBuilder.EmitStopPoint(irs->func()->decl->endloc); + + irs->ir->CreateRet(loadFromSlot ? + DtoLoad(irs->func()->retValSlot) : returnValue); + } + else + { + irs->ir->CreateRetVoid(); + } + + // TODO: Should not be needed llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterreturn", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -494,7 +295,7 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); @@ -518,7 +319,7 @@ public: LOG_SCOPE; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); emitCoverageLinecountInc(stmt->loc); if (stmt->match) @@ -527,45 +328,45 @@ public: DValue* cond_e = toElemDtor(stmt->condition); LLValue* cond_val = cond_e->getRVal(); - llvm::BasicBlock* ifbb = llvm::BasicBlock::Create(gIR->context(), "if", gIR->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endif", gIR->topfunc()); - llvm::BasicBlock* elsebb = stmt->elsebody ? llvm::BasicBlock::Create(gIR->context(), "else", gIR->topfunc(), endbb) : endbb; + llvm::BasicBlock* ifbb = llvm::BasicBlock::Create(irs->context(), "if", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endif", irs->topfunc()); + llvm::BasicBlock* elsebb = stmt->elsebody ? llvm::BasicBlock::Create(irs->context(), "else", irs->topfunc(), endbb) : endbb; - if (cond_val->getType() != LLType::getInt1Ty(gIR->context())) { + if (cond_val->getType() != LLType::getInt1Ty(irs->context())) { IF_LOG Logger::cout() << "if conditional: " << *cond_val << '\n'; cond_val = DtoCast(stmt->loc, cond_e, Type::tbool)->getRVal(); } - llvm::BranchInst::Create(ifbb, elsebb, cond_val, gIR->scopebb()); + llvm::BranchInst::Create(ifbb, elsebb, cond_val, irs->scopebb()); // replace current scope - gIR->scope() = IRScope(ifbb); + irs->scope() = IRScope(ifbb); // do scoped statements if (stmt->ifbody) { - gIR->DBuilder.EmitBlockStart(stmt->ifbody->loc); + irs->DBuilder.EmitBlockStart(stmt->ifbody->loc); stmt->ifbody->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } - if (!gIR->scopereturned()) { - llvm::BranchInst::Create(endbb, gIR->scopebb()); + if (!irs->scopereturned()) { + llvm::BranchInst::Create(endbb, irs->scopebb()); } if (stmt->elsebody) { - gIR->scope() = IRScope(elsebb); - gIR->DBuilder.EmitBlockStart(stmt->elsebody->loc); + irs->scope() = IRScope(elsebb); + irs->DBuilder.EmitBlockStart(stmt->elsebody->loc); stmt->elsebody->accept(this); - if (!gIR->scopereturned()) { - llvm::BranchInst::Create(endbb, gIR->scopebb()); + if (!irs->scopereturned()) { + llvm::BranchInst::Create(endbb, irs->scopebb()); } - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); // rewrite the scope - gIR->scope() = IRScope(endbb); + irs->scope() = IRScope(endbb); } ////////////////////////////////////////////////////////////////////////// @@ -575,9 +376,9 @@ public: LOG_SCOPE; if (stmt->statement) { - gIR->DBuilder.EmitBlockStart(stmt->statement->loc); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); stmt->statement->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } } @@ -588,19 +389,19 @@ public: LOG_SCOPE; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); // create while blocks - llvm::BasicBlock* whilebb = llvm::BasicBlock::Create(gIR->context(), "whilecond", gIR->topfunc()); - llvm::BasicBlock* whilebodybb = llvm::BasicBlock::Create(gIR->context(), "whilebody", gIR->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endwhile", gIR->topfunc()); + llvm::BasicBlock* whilebb = llvm::BasicBlock::Create(irs->context(), "whilecond", irs->topfunc()); + llvm::BasicBlock* whilebodybb = llvm::BasicBlock::Create(irs->context(), "whilebody", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endwhile", irs->topfunc()); // move into the while block irs->ir->CreateBr(whilebb); // replace current scope - gIR->scope() = IRScope(whilebb); + irs->scope() = IRScope(whilebb); // create the condition emitCoverageLinecountInc(stmt->condition->loc); @@ -612,23 +413,23 @@ public: llvm::BranchInst::Create(whilebodybb, endbb, cond_val, irs->scopebb()); // rewrite scope - gIR->scope() = IRScope(whilebodybb); + irs->scope() = IRScope(whilebodybb); // while body code - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, whilebb, endbb)); + irs->func()->scopes->pushLoopTarget(stmt, whilebb, endbb); if (stmt->body) stmt->body->accept(this); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); // loop - if (!gIR->scopereturned()) - llvm::BranchInst::Create(whilebb, gIR->scopebb()); + if (!irs->scopereturned()) + llvm::BranchInst::Create(whilebb, irs->scopebb()); // rewrite the scope - gIR->scope() = IRScope(endbb); + irs->scope() = IRScope(endbb); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -638,29 +439,29 @@ public: LOG_SCOPE; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); // create while blocks - llvm::BasicBlock* dowhilebb = llvm::BasicBlock::Create(gIR->context(), "dowhile", gIR->topfunc()); - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "dowhilecond", gIR->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "enddowhile", gIR->topfunc()); + llvm::BasicBlock* dowhilebb = llvm::BasicBlock::Create(irs->context(), "dowhile", irs->topfunc()); + llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "dowhilecond", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "enddowhile", irs->topfunc()); // move into the while block - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(dowhilebb, gIR->scopebb()); + assert(!irs->scopereturned()); + llvm::BranchInst::Create(dowhilebb, irs->scopebb()); // replace current scope - gIR->scope() = IRScope(dowhilebb); + irs->scope() = IRScope(dowhilebb); // do-while body code - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, condbb, endbb)); + irs->func()->scopes->pushLoopTarget(stmt, condbb, endbb); if (stmt->body) stmt->body->accept(this); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); // branch to condition block - llvm::BranchInst::Create(condbb, gIR->scopebb()); - gIR->scope() = IRScope(condbb); + llvm::BranchInst::Create(condbb, irs->scopebb()); + irs->scope() = IRScope(condbb); // create the condition emitCoverageLinecountInc(stmt->condition->loc); @@ -669,13 +470,13 @@ public: delete cond_e; // conditional branch - llvm::BranchInst::Create(dowhilebb, endbb, cond_val, gIR->scopebb()); + llvm::BranchInst::Create(dowhilebb, endbb, cond_val, irs->scopebb()); // rewrite the scope - gIR->scope() = IRScope(endbb); + irs->scope() = IRScope(endbb); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -685,21 +486,21 @@ public: LOG_SCOPE; // start new dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); // create for blocks - llvm::BasicBlock* forbb = llvm::BasicBlock::Create(gIR->context(), "forcond", gIR->topfunc()); - llvm::BasicBlock* forbodybb = llvm::BasicBlock::Create(gIR->context(), "forbody", gIR->topfunc()); - llvm::BasicBlock* forincbb = llvm::BasicBlock::Create(gIR->context(), "forinc", gIR->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endfor", gIR->topfunc()); + llvm::BasicBlock* forbb = llvm::BasicBlock::Create(irs->context(), "forcond", irs->topfunc()); + llvm::BasicBlock* forbodybb = llvm::BasicBlock::Create(irs->context(), "forbody", irs->topfunc()); + llvm::BasicBlock* forincbb = llvm::BasicBlock::Create(irs->context(), "forinc", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "endfor", irs->topfunc()); // init if (stmt->init != 0) stmt->init->accept(this); // move into the for condition block, ie. start the loop - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(forbb, gIR->scopebb()); + assert(!irs->scopereturned()); + llvm::BranchInst::Create(forbb, irs->scopebb()); // In case of loops that have been rewritten to a composite statement // containing the initializers and then the actual loop, we need to @@ -709,11 +510,10 @@ public: { scopeStart = scope->statement; } - irs->func()->gen->targetScopes.push_back(IRTargetScope( - scopeStart, NULL, forincbb, endbb)); + irs->func()->scopes->pushLoopTarget(scopeStart, forincbb, endbb); // replace current scope - gIR->scope() = IRScope(forbb); + irs->scope() = IRScope(forbb); // create the condition llvm::Value* cond_val; @@ -730,20 +530,20 @@ public: } // conditional branch - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(forbodybb, endbb, cond_val, gIR->scopebb()); + assert(!irs->scopereturned()); + llvm::BranchInst::Create(forbodybb, endbb, cond_val, irs->scopebb()); // rewrite scope - gIR->scope() = IRScope(forbodybb); + irs->scope() = IRScope(forbodybb); // do for body code if (stmt->body) stmt->body->accept(this); // move into the for increment block - if (!gIR->scopereturned()) - llvm::BranchInst::Create(forincbb, gIR->scopebb()); - gIR->scope() = IRScope(forincbb); + if (!irs->scopereturned()) + llvm::BranchInst::Create(forincbb, irs->scopebb()); + irs->scope() = IRScope(forincbb); // increment if (stmt->increment) { @@ -753,16 +553,16 @@ public: } // loop - if (!gIR->scopereturned()) - llvm::BranchInst::Create(forbb, gIR->scopebb()); + if (!irs->scopereturned()) + llvm::BranchInst::Create(forbb, irs->scopebb()); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); // rewrite the scope - gIR->scope() = IRScope(endbb); + irs->scope() = IRScope(endbb); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -777,58 +577,26 @@ public: return; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - if (stmt->ident != 0) { + if (stmt->ident) { IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); - DtoEnclosingHandlers(stmt->loc, stmt->target); - - // get the loop statement the label refers to - Statement* targetLoopStatement = stmt->target->statement; + // Get the loop or break statement the label refers to + Statement* targetStatement = stmt->target->statement; ScopeStatement* tmp; - while((tmp = targetLoopStatement->isScopeStatement())) - targetLoopStatement = tmp->statement; + while((tmp = targetStatement->isScopeStatement())) + targetStatement = tmp->statement; - // find the right break block and jump there - // the right break block is found in the nearest scope to the LabelStatement - // with onlyLabelBreak == true. Therefore the search starts at the outer - // scope (in contract to most other searches, which start with the inner - // scope). This code is tested by test runnable/foreach5.d, test9068(). - bool found = false; - FuncGen::TargetScopeVec::iterator it = irs->func()->gen->targetScopes.begin(); - FuncGen::TargetScopeVec::iterator it_end = irs->func()->gen->targetScopes.end(); - while (it != it_end && it->s != stmt->target) - ++it; - assert(it != it_end && "Labeled break but no label found"); - while (it != it_end) { - if (it->onlyLabeledBreak || it->s == targetLoopStatement) { - llvm::BranchInst::Create(it->breakTarget, irs->scopebb()); - found = true; - break; - } - ++it; - } - assert(found && "Labeled break but no jump target found"); - } - else { - // find closest scope with a break target - FuncGen::TargetScopeVec::reverse_iterator it = irs->func()->gen->targetScopes.rbegin(); - FuncGen::TargetScopeVec::reverse_iterator it_end = irs->func()->gen->targetScopes.rend(); - while (it != it_end) { - if (it->breakTarget && !it->onlyLabeledBreak) { - break; - } - ++it; - } - DtoEnclosingHandlers(stmt->loc, it->s); - llvm::BranchInst::Create(it->breakTarget, gIR->scopebb()); + irs->func()->scopes->breakToStatement(targetStatement); + } else { + irs->func()->scopes->breakToClosest(); } // the break terminated this basicblock, start a new one - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterbreak", irs->topfunc()); + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -839,11 +607,11 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - if (stmt->ident != 0) { + if (stmt->ident) { IF_LOG Logger::println("ident = %s", stmt->ident->toChars()); // get the loop statement the label refers to @@ -852,40 +620,13 @@ public: while((tmp = targetLoopStatement->isScopeStatement())) targetLoopStatement = tmp->statement; - // find the right continue block - bool found = false; - FuncGen::TargetScopeVec::reverse_iterator it = irs->func()->gen->targetScopes.rbegin(); - FuncGen::TargetScopeVec::reverse_iterator it_end = irs->func()->gen->targetScopes.rend(); - while (it != it_end) { - if (it->s == targetLoopStatement) { - found = true; - break; - } - ++it; - } - - assert(found); - // emit destructors and finally statements - DtoEnclosingHandlers(stmt->loc, it->s); - // jump to the continue block - llvm::BranchInst::Create(it->continueTarget, gIR->scopebb()); - } - else { - // find closest scope with a continue target - FuncGen::TargetScopeVec::reverse_iterator it = irs->func()->gen->targetScopes.rbegin(); - FuncGen::TargetScopeVec::reverse_iterator it_end = irs->func()->gen->targetScopes.rend(); - while (it != it_end) { - if (it->continueTarget) { - break; - } - ++it; - } - DtoEnclosingHandlers(stmt->loc, it->s); - llvm::BranchInst::Create(it->continueTarget, gIR->scopebb()); + irs->func()->scopes->continueWithLoop(targetLoopStatement); + } else { + irs->func()->scopes->continueWithClosest(); } - // the continue terminated this basicblock, start a new one - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftercontinue", irs->topfunc()); + // the break terminated this basicblock, start a new one + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterbreak", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -903,87 +644,56 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); - // if there's no finalbody or no body, things are simple - if (!stmt->finalbody) { + // We only need to consider exception handling/cleanup issues if there + // is both a try and a finally block. If not, just directly emit what + // is present. + if (!stmt->body || !stmt->finalbody) { if (stmt->body) { - gIR->DBuilder.EmitBlockStart(stmt->body->loc); + irs->DBuilder.EmitBlockStart(stmt->body->loc); stmt->body->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); + } else if (stmt->finalbody) { + irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); + stmt->finalbody->accept(this); + irs->DBuilder.EmitBlockEnd(); } return; } - if (!stmt->body) { - gIR->DBuilder.EmitBlockStart(stmt->finalbody->loc); - stmt->finalbody->accept(this); - gIR->DBuilder.EmitBlockEnd(); - return; - } - // create basic blocks - llvm::BasicBlock* trybb = llvm::BasicBlock::Create(gIR->context(), "try", irs->topfunc()); - llvm::BasicBlock* finallybb = llvm::BasicBlock::Create(gIR->context(), "finally", irs->topfunc()); - // the landing pad for statements in the try block - llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endtryfinally", irs->topfunc()); + // We'll append the "try" part to the current basic block later. No need + // for an extra one (we'd need to branch to it unconditionally anyway). + llvm::BasicBlock* trybb = irs->scopebb(); - // pass the previous BB into this - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(trybb, irs->scopebb()); + // Emit the finally block and set up the cleanup scope for it. + llvm::BasicBlock* finallybb = + llvm::BasicBlock::Create(irs->context(), "finally", irs->topfunc()); + irs->scope() = IRScope(finallybb); + irs->DBuilder.EmitBlockStart(stmt->finalbody->loc); + stmt->finalbody->accept(this); + irs->DBuilder.EmitBlockEnd(); - // - // set up the landing pad - // - irs->scope() = IRScope(landingpadbb); + CleanupCursor cleanupBefore = irs->func()->scopes->currentCleanupScope(); + irs->func()->scopes->pushCleanup(finallybb, irs->scopebb()); - assert(stmt->finalbody); - IRLandingPad& pad = gIR->func()->gen->landingPadInfo; - pad.addFinally(stmt->finalbody); - pad.push(landingpadbb); - gIR->func()->gen->targetScopes.push_back( - IRTargetScope( - stmt, - new EnclosingTryFinally(stmt, gIR->func()->gen->landingPad), - NULL, - endbb, - true - ) - ); - - // - // do the try block - // + // Emit the try block. irs->scope() = IRScope(trybb); assert(stmt->body); - gIR->DBuilder.EmitBlockStart(stmt->body->loc); + irs->DBuilder.EmitBlockStart(stmt->body->loc); stmt->body->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); - // terminate try BB - if (!irs->scopereturned()) - llvm::BranchInst::Create(finallybb, irs->scopebb()); - - pad.pop(); - gIR->func()->gen->targetScopes.pop_back(); - - // - // do finally block - // - irs->scope() = IRScope(finallybb); - gIR->DBuilder.EmitBlockStart(stmt->finalbody->loc); - stmt->finalbody->accept(this); - gIR->DBuilder.EmitBlockEnd(); - - // terminate finally - //TODO: isn't it an error to have a 'returned' finally block? - if (!gIR->scopereturned()) { - llvm::BranchInst::Create(endbb, irs->scopebb()); + // Create a block to branch to after successfully running the try block + // and any cleanups. + if (!irs->scopereturned()) { + llvm::BasicBlock* successbb = llvm::BasicBlock::Create(irs->context(), + "try.success", irs->topfunc()); + irs->func()->scopes->runCleanups(cleanupBefore, successbb); + irs->scope() = IRScope(successbb); } - - // rewrite the scope - irs->scope() = IRScope(endbb); + irs->func()->scopes->popCleanups(cleanupBefore); } ////////////////////////////////////////////////////////////////////////// @@ -992,52 +702,87 @@ public: IF_LOG Logger::println("TryCatchStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + // Emit dwarf stop point + irs->DBuilder.EmitStopPoint(stmt->loc); - // create basic blocks - llvm::BasicBlock* trybb = llvm::BasicBlock::Create(gIR->context(), "try", irs->topfunc()); - // the landing pad will be responsible for branching to the correct catch block - llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endtrycatch", irs->topfunc()); + // We'll append the "try" part to the current basic block later. No need + // for an extra one (we'd need to branch to it unconditionally anyway). + llvm::BasicBlock* trybb = irs->scopebb(); - // pass the previous BB into this - assert(!gIR->scopereturned()); - llvm::BranchInst::Create(trybb, irs->scopebb()); + // Create a basic block to branch to after leaving the try or an + // associated catch block successfully. + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), + "try.success.or.caught", irs->topfunc()); - // - // set up the landing pad - // assert(stmt->catches); - gIR->scope() = IRScope(landingpadbb); - IRLandingPad& pad = gIR->func()->gen->landingPadInfo; - for (Catches::iterator I = stmt->catches->begin(), - E = stmt->catches->end(); - I != E; ++I) - { - Catch *c = *I; - pad.addCatch(c, endbb); + for (Catches::reverse_iterator it = stmt->catches->rbegin(), + end = stmt->catches->rend(); + it != end; ++it + ) { + llvm::BasicBlock* catchBlock = llvm::BasicBlock::Create(irs->context(), + llvm::Twine("catch.") + (*it)->type->toChars(), + irs->topfunc(), endbb); + + irs->scope() = IRScope(catchBlock); + irs->DBuilder.EmitBlockStart((*it)->loc); + + llvm::Function* enterCatchFn = + LLVM_D_GetRuntimeFunction(Loc(), irs->module, "_d_eh_enter_catch"); + irs->ir->CreateCall(enterCatchFn); + + // For catches that use the Throwable object, create storage for it. + // We will set it in the code that branches from the landing pads + // (there might be more than one) to catchBlock. + llvm::Value* exceptionVar = 0; + if ((*it)->var) { + DtoDeclarationExp((*it)->var); + IrLocal* irLocal = getIrLocal((*it)->var); + exceptionVar = irLocal->value; + } + + // emit handler, if there is one + // handler is zero for instance for 'catch { debug foo(); }' + if ((*it)->handler) { + Statement_toIR((*it)->handler, irs); + } + + if (!irs->scopereturned()) { + irs->ir->CreateBr(endbb); + } + + irs->DBuilder.EmitBlockEnd(); + + ClassDeclaration* catchType = + (*it)->type->toBasetype()->isClassHandle(); + DtoResolveClass(catchType); + + irs->func()->scopes->pushCatch( + getIrAggr(catchType)->getClassInfoSymbol(), exceptionVar, + catchBlock); } - pad.push(landingpadbb); - - // - // do the try block - // + // Emit the try block. irs->scope() = IRScope(trybb); assert(stmt->body); - gIR->DBuilder.EmitBlockStart(stmt->body->loc); + irs->DBuilder.EmitBlockStart(stmt->body->loc); stmt->body->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); - if (!gIR->scopereturned()) + if (!irs->scopereturned()) { llvm::BranchInst::Create(endbb, irs->scopebb()); + } - pad.pop(); + // Now that we have done the try block, remove the catches and continue + // codegen in the end block the try and all the catches branch to. + for (Catches::reverse_iterator it = stmt->catches->rbegin(), + end = stmt->catches->rend(); + it != end; ++it + ) { + irs->func()->scopes->popCatch(); + } - // rewrite the scope irs->scope() = IRScope(endbb); } @@ -1048,22 +793,20 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); assert(stmt->exp); DValue* e = toElemDtor(stmt->exp); - llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, gIR->module, "_d_throw_exception"); - //Logger::cout() << "calling: " << *fn << '\n'; - LLValue* arg = DtoBitCast(e->getRVal(), fn->getFunctionType()->getParamType(0)); - //Logger::cout() << "arg: " << *arg << '\n'; - gIR->CreateCallOrInvoke(fn, arg); - gIR->ir->CreateUnreachable(); + llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_throw_exception"); + LLValue* arg = DtoBitCast(e->getRVal(), fn->getFunctionType()->getParamType(0));; + irs->CreateCallOrInvoke(fn, arg); + irs->ir->CreateUnreachable(); - // need a block after the throw for now - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterthrow", irs->topfunc()); + // TODO: Should not be needed. + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "afterthrow", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -1074,21 +817,11 @@ public: LOG_SCOPE; // emit dwarf stop point - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - llvm::BasicBlock* oldbb = gIR->scopebb(); - - // clear data from previous passes... :/ - for (CaseStatements::iterator I = stmt->cases->begin(), - E = stmt->cases->end(); - I != E; ++I) - { - CaseStatement *cs = *I; - cs->bodyBB = NULL; - cs->llvmIdx = NULL; - } + llvm::BasicBlock* oldbb = irs->scopebb(); // If one of the case expressions is non-constant, we can't use // 'switch' instruction (that can happen because D2 allows to @@ -1110,29 +843,29 @@ public: // body block. // FIXME: that block is never used - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "switchbody", irs->topfunc()); + llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "switchbody", irs->topfunc()); // default llvm::BasicBlock* defbb = 0; if (stmt->sdefault) { Logger::println("has default"); - defbb = llvm::BasicBlock::Create(gIR->context(), "default", irs->topfunc()); + defbb = llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); stmt->sdefault->bodyBB = defbb; } // end (break point) - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "switchend", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "switchend", irs->topfunc()); // do switch body assert(stmt->body); irs->scope() = IRScope(bodybb); - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, NULL, endbb)); + irs->func()->scopes->pushBreakTarget(stmt, endbb); stmt->body->accept(this); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popBreakTarget(); if (!irs->scopereturned()) llvm::BranchInst::Create(endbb, irs->scopebb()); - gIR->scope() = IRScope(oldbb); + irs->scope() = IRScope(oldbb); if (useSwitchInst) { // string switch? @@ -1165,14 +898,14 @@ public: LLType* elemTy = DtoType(stmt->condition->type); LLArrayType* arrTy = llvm::ArrayType::get(elemTy, inits.size()); LLConstant* arrInit = LLConstantArray::get(arrTy, inits); - LLGlobalVariable* arr = new llvm::GlobalVariable(gIR->module, arrTy, true, llvm::GlobalValue::InternalLinkage, arrInit, ".string_switch_table_data"); + LLGlobalVariable* arr = new llvm::GlobalVariable(irs->module, arrTy, true, llvm::GlobalValue::InternalLinkage, arrInit, ".string_switch_table_data"); LLType* elemPtrTy = getPtrToType(elemTy); LLConstant* arrPtr = llvm::ConstantExpr::getBitCast(arr, elemPtrTy); // build the static table LLType* types[] = { DtoSize_t(), elemPtrTy }; - LLStructType* sTy = llvm::StructType::get(gIR->context(), types, false); + LLStructType* sTy = llvm::StructType::get(irs->context(), types, false); LLConstant* sinits[] = { DtoConstSize_t(inits.size()), arrPtr }; switchTable = llvm::ConstantStruct::get(sTy, llvm::ArrayRef(sinits)); } @@ -1204,7 +937,7 @@ public: DValue* cond = toElemDtor(stmt->condition); LLValue *condVal = cond->getRVal(); - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "checkcase", irs->topfunc()); + llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc()); llvm::BranchInst::Create(nextbb, irs->scopebb()); irs->scope() = IRScope(nextbb); @@ -1215,7 +948,7 @@ public: CaseStatement *cs = *I; LLValue *cmp = irs->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, cs->llvmIdx, condVal, "checkcase"); - nextbb = llvm::BasicBlock::Create(gIR->context(), "checkcase", irs->topfunc()); + nextbb = llvm::BasicBlock::Create(irs->context(), "checkcase", irs->topfunc()); llvm::BranchInst::Create(cs->bodyBB, nextbb, cmp, irs->scopebb()); irs->scope() = IRScope(nextbb); } @@ -1228,7 +961,7 @@ public: endbb->moveAfter(nextbb); } - gIR->scope() = IRScope(endbb); + irs->scope() = IRScope(endbb); } ////////////////////////////////////////////////////////////////////////// @@ -1237,7 +970,7 @@ public: IF_LOG Logger::println("CaseStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - llvm::BasicBlock* nbb = llvm::BasicBlock::Create(gIR->context(), "case", irs->topfunc()); + llvm::BasicBlock* nbb = llvm::BasicBlock::Create(irs->context(), "case", irs->topfunc()); if (stmt->bodyBB && !stmt->bodyBB->getTerminator()) { llvm::BranchInst::Create(nbb, stmt->bodyBB); @@ -1255,10 +988,10 @@ public: irs->scope() = IRScope(stmt->bodyBB); assert(stmt->statement); - gIR->DBuilder.EmitBlockStart(stmt->statement->loc); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); emitCoverageLinecountInc(stmt->loc); stmt->statement->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -1269,7 +1002,7 @@ public: assert(stmt->bodyBB); - llvm::BasicBlock* nbb = llvm::BasicBlock::Create(gIR->context(), "default", irs->topfunc()); + llvm::BasicBlock* nbb = llvm::BasicBlock::Create(irs->context(), "default", irs->topfunc()); if (!stmt->bodyBB->getTerminator()) { @@ -1283,10 +1016,10 @@ public: irs->scope() = IRScope(stmt->bodyBB); assert(stmt->statement); - gIR->DBuilder.EmitBlockStart(stmt->statement->loc); + irs->DBuilder.EmitBlockStart(stmt->statement->loc); emitCoverageLinecountInc(stmt->loc); stmt->statement->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -1300,7 +1033,7 @@ public: return; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); // DMD doesn't fold stuff like continue/break, and since this isn't really a loop // we have to keep track of each statement and jump to the next/end on continue/break @@ -1311,11 +1044,11 @@ public: for (size_t i=0; i < nstmt; i++) { - blocks[i] = llvm::BasicBlock::Create(gIR->context(), "unrolledstmt", irs->topfunc()); + blocks[i] = llvm::BasicBlock::Create(irs->context(), "unrolledstmt", irs->topfunc()); } // create end block - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "unrolledend", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "unrolledend", irs->topfunc()); // enter first stmt if (!irs->scopereturned()) @@ -1337,13 +1070,13 @@ public: // push loop scope // continue goes to next statement, break goes to end - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, nextbb, endbb)); + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); // do statement s->accept(this); // pop loop scope - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); // next stmt if (!irs->scopereturned()) @@ -1356,7 +1089,7 @@ public: irs->scope() = IRScope(endbb); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -1366,7 +1099,7 @@ public: LOG_SCOPE; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); //assert(arguments->dim == 1); assert(stmt->value != 0); @@ -1408,11 +1141,11 @@ public: size_t sz1 = getTypeBitSize(niters->getType()); size_t sz2 = getTypeBitSize(keytype); if (sz1 < sz2) - niters = gIR->ir->CreateZExt(niters, keytype, "foreachtrunckey"); + niters = irs->ir->CreateZExt(niters, keytype, "foreachtrunckey"); else if (sz1 > sz2) - niters = gIR->ir->CreateTrunc(niters, keytype, "foreachtrunckey"); + niters = irs->ir->CreateTrunc(niters, keytype, "foreachtrunckey"); else - niters = gIR->ir->CreateBitCast(niters, keytype, "foreachtrunckey"); + niters = irs->ir->CreateBitCast(niters, keytype, "foreachtrunckey"); } if (stmt->op == TOKforeach) { @@ -1422,10 +1155,10 @@ public: new llvm::StoreInst(niters, keyvar, irs->scopebb()); } - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "foreachcond", irs->topfunc()); - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "foreachbody", irs->topfunc()); - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "foreachnext", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "foreachend", irs->topfunc()); + llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "foreachcond", irs->topfunc()); + llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "foreachbody", irs->topfunc()); + llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "foreachnext", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "foreachend", irs->topfunc()); llvm::BranchInst::Create(condbb, irs->scopebb()); @@ -1463,10 +1196,10 @@ public: } // emit body - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, nextbb, endbb)); + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); if (stmt->body) stmt->body->accept(this); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); if (!irs->scopereturned()) llvm::BranchInst::Create(nextbb, irs->scopebb()); @@ -1481,7 +1214,7 @@ public: llvm::BranchInst::Create(condbb, irs->scopebb()); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); // end irs->scope() = IRScope(endbb); @@ -1494,7 +1227,7 @@ public: LOG_SCOPE; // start a dwarf lexical block - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); // evaluate lwr/upr assert(stmt->lwr->type->isintegral()); @@ -1513,10 +1246,10 @@ public: DtoStore(upper, keyval); // set up the block we'll need - llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_cond", irs->topfunc()); - llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_body", irs->topfunc()); - llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_next", irs->topfunc()); - llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_end", irs->topfunc()); + llvm::BasicBlock* condbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_cond", irs->topfunc()); + llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(irs->context(), "foreachrange_body", irs->topfunc()); + llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_next", irs->topfunc()); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(irs->context(), "foreachrange_end", irs->topfunc()); // jump to condition llvm::BranchInst::Create(condbb, irs->scopebb()); @@ -1558,10 +1291,10 @@ public: } // emit body - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, nextbb, endbb)); + irs->func()->scopes->pushLoopTarget(stmt, nextbb, endbb); if (stmt->body) stmt->body->accept(this); - irs->func()->gen->targetScopes.pop_back(); + irs->func()->scopes->popLoopTarget(); // jump to next iteration if (!irs->scopereturned()) @@ -1583,7 +1316,7 @@ public: llvm::BranchInst::Create(condbb, irs->scopebb()); // end the dwarf lexical block - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); // END irs->scope() = IRScope(endbb); @@ -1607,15 +1340,13 @@ public: irs->asmBlock->internalLabels.push_back(stmt->ident); // disable inlining - gIR->func()->setNeverInline(); + irs->func()->setNeverInline(); } else { - std::string labelname = irs->func()->gen->getScopedLabelName(stmt->ident->toChars()); - llvm::BasicBlock*& labelBB = irs->func()->gen->labelToBB[labelname]; - - if (!labelBB) - labelBB = llvm::BasicBlock::Create(gIR->context(), "label_" + labelname, irs->topfunc()); + llvm::BasicBlock* labelBB = llvm::BasicBlock::Create(irs->context(), + llvm::Twine("label.") + stmt->ident->toChars(), irs->topfunc()); + irs->func()->scopes->addLabelTarget(stmt->ident, labelBB); if (!irs->scopereturned()) llvm::BranchInst::Create(labelBB, irs->scopebb()); @@ -1624,9 +1355,7 @@ public: } if (stmt->statement) { - irs->func()->gen->targetScopes.push_back(IRTargetScope(stmt, NULL, NULL, NULL)); stmt->statement->accept(this); - irs->func()->gen->targetScopes.pop_back(); } } @@ -1636,14 +1365,14 @@ public: IF_LOG Logger::println("GotoStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergoto", irs->topfunc()); - - DtoGoto(stmt->loc, stmt->label, stmt->tf); + DtoGoto(stmt->loc, stmt->label); + // TODO: Should not be needed. + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergoto", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -1653,18 +1382,22 @@ public: IF_LOG Logger::println("GotoDefaultStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergotodefault", irs->topfunc()); - assert(!irs->scopereturned()); assert(stmt->sw->sdefault->bodyBB); +#if 0 + // TODO: Store switch scopes. DtoEnclosingHandlers(stmt->loc, stmt->sw); +#endif llvm::BranchInst::Create(stmt->sw->sdefault->bodyBB, irs->scopebb()); + + // TODO: Should not be needed. + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergotodefault", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -1674,21 +1407,25 @@ public: IF_LOG Logger::println("GotoCaseStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - gIR->DBuilder.EmitStopPoint(stmt->loc); + irs->DBuilder.EmitStopPoint(stmt->loc); emitCoverageLinecountInc(stmt->loc); - llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergotocase", irs->topfunc()); - assert(!irs->scopereturned()); if (!stmt->cs->bodyBB) { - stmt->cs->bodyBB = llvm::BasicBlock::Create(gIR->context(), "goto_case", irs->topfunc()); + stmt->cs->bodyBB = llvm::BasicBlock::Create(irs->context(), "goto_case", irs->topfunc()); } +#if 0 + // TODO: Store switch scopes. DtoEnclosingHandlers(stmt->loc, stmt->sw); +#endif llvm::BranchInst::Create(stmt->cs->bodyBB, irs->scopebb()); + + // TODO: Should not be needed. + llvm::BasicBlock* bb = llvm::BasicBlock::Create(irs->context(), "aftergotocase", irs->topfunc()); irs->scope() = IRScope(bb); } @@ -1698,7 +1435,7 @@ public: IF_LOG Logger::println("WithStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - gIR->DBuilder.EmitBlockStart(stmt->loc); + irs->DBuilder.EmitBlockStart(stmt->loc); assert(stmt->exp); @@ -1713,7 +1450,7 @@ public: if (stmt->body) stmt->body->accept(this); - gIR->DBuilder.EmitBlockEnd(); + irs->DBuilder.EmitBlockEnd(); } ////////////////////////////////////////////////////////////////////////// @@ -1722,9 +1459,9 @@ public: IF_LOG Logger::println("SwitchErrorStatement::toIR(): %s", stmt->loc.toChars()); LOG_SCOPE; - llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, gIR->module, "_d_switch_error"); + llvm::Function* fn = LLVM_D_GetRuntimeFunction(stmt->loc, irs->module, "_d_switch_error"); - LLValue *moduleInfoSymbol = getIrModule(gIR->func()->decl->getModule())->moduleInfoSymbol(); + LLValue *moduleInfoSymbol = getIrModule(irs->func()->decl->getModule())->moduleInfoSymbol(); LLType *moduleInfoType = DtoType(Module::moduleinfo->type); LLValue* args[] = { @@ -1735,7 +1472,7 @@ public: }; // call - LLCallSite call = gIR->CreateCallOrInvoke(fn, args); + LLCallSite call = irs->CreateCallOrInvoke(fn, args); call.setDoesNotReturn(); } @@ -1774,13 +1511,6 @@ public: ////////////////////////////////////////////////////////////////////////////// -void codegenFunction(Statement *s, IRState *irs) -{ - FindEnclosingTryFinally v; - s->accept(&v); - Statement_toIR(s, irs); -} - void Statement_toIR(Statement *s, IRState *irs) { ToIRVisitor v(irs); diff --git a/gen/toir.cpp b/gen/toir.cpp index 48e00d918d..fc9e2c1621 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -39,7 +39,6 @@ #include "gen/warnings.h" #include "ir/irtypeclass.h" #include "ir/irtypestruct.h" -#include "ir/irlandingpad.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include @@ -267,15 +266,26 @@ class ToElemVisitor : public Visitor { IRState *p; bool destructTemporaries; + CleanupCursor initialCleanupScope; DValue *result; public: ToElemVisitor(IRState *p_, bool destructTemporaries_) : p(p_), destructTemporaries(destructTemporaries_), result(NULL) { - p->func()->gen->pushToElemScope(); + initialCleanupScope = p->func()->scopes->currentCleanupScope(); } - ~ToElemVisitor() { p->func()->gen->popToElemScope(destructTemporaries); } + ~ToElemVisitor() + { + if (destructTemporaries && p->func()->scopes->currentCleanupScope() != initialCleanupScope) + { + llvm::BasicBlock* endbb = llvm::BasicBlock::Create( + p->context(), "toElem.success", p->topfunc()); + p->func()->scopes->runCleanups(initialCleanupScope, endbb); + p->func()->scopes->popCleanups(initialCleanupScope); + p->scope() = IRScope(endbb); + } + } DValue *getResult() { return result; } @@ -300,7 +310,17 @@ public: { VarDeclaration* vd = varValue->var; if (!vd->isDataseg() && vd->edtor && !vd->noscope) - p->func()->gen->pushTemporaryToDestruct(vd); + { + llvm::BasicBlock* cleanupbb = llvm::BasicBlock::Create( + p->context(), llvm::Twine("dtor.") + vd->toChars(), p->topfunc()); + + // TODO: Clean this up with push/pop insertion point methods. + IRScope oldScope = p->scope(); + p->scope() = IRScope(cleanupbb); + toElemDtor(vd->edtor); + p->func()->scopes->pushCleanup(cleanupbb, p->scopebb()); + p->scope() = oldScope; + } } } } @@ -1148,13 +1168,13 @@ public: if (result) return; - VarDeclarations& temporaries = gIR->func()->gen->getTemporariesToDestruct(); - +#if 0 // check if we are about to construct a just declared temporary: // MyStruct(myArgs) => (MyStruct tmp; tmp).this(myArgs) + const CleanupCursor temporaryScope = p->func()->scopes->currentCleanupScope(); bool constructingTemporary = false; - if (!temporaries.empty() && - dfnval && dfnval->func && dfnval->func->isCtorDeclaration()) + if (temporaryScope != 0 && dfnval && dfnval->func && + dfnval->func->isCtorDeclaration()) { DotVarExp* dve = static_cast(e->e1); if (dve->e1->op == TOKcomma) @@ -1175,17 +1195,19 @@ public: // i.e., don't destruct the temporary if its constructor throws // (DMD issue 13095) // => remember position in stack and pop temporarily - int indexOfTemporary = (!constructingTemporary ? -1 - : static_cast(temporaries.size()) - 1); - VarDeclaration* temporary = (!constructingTemporary ? NULL - : temporaries.pop()); - + if (constructingTemporary) { + p->func()->scopes->suspendCleanup(temporaryScope); + } +#endif result = DtoCallFunction(e->loc, e->type, fnval, e->arguments); +#if 0 // insert the now fully constructed temporary at the original index; // i.e., before any new temporaries pushed by DtoCallFunction() - if (constructingTemporary) - temporaries.insert(indexOfTemporary, temporary); + if (constructingTemporary) { + p->func()->scopes->resumeCleanup(temporaryScope); + } +#endif } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/ir/irfunction.cpp b/ir/irfunction.cpp index 0e520ad2a9..901b5b2d09 100644 --- a/ir/irfunction.cpp +++ b/ir/irfunction.cpp @@ -15,117 +15,251 @@ #include "ir/irfunction.h" #include -FuncGen::FuncGen() -{ - landingPad = NULL; - nextUnique.push(0); -} +namespace { +void executeCleanup(IRState *irs, CleanupScope& scope, + llvm::BasicBlock *sourceBlock, llvm::BasicBlock* continueWith +) { + if (scope.exitTargets.empty() || ( + scope.exitTargets.size() == 1 && + scope.exitTargets[0].branchTarget == continueWith + )) { + // We didn't need a branch selector before and still don't need one. + assert(!scope.branchSelector); -std::string FuncGen::getScopedLabelName(const char* ident) -{ - if(labelScopes.empty()) - return std::string(ident); - - std::string result = "__"; - for(unsigned int i = 0; i < labelScopes.size(); ++i) - result += labelScopes[i] + "_"; - return result + ident; -} - -void FuncGen::pushUniqueLabelScope(const char* name) -{ - std::ostringstream uniquename; - uniquename << name << nextUnique.top()++; - nextUnique.push(0); - labelScopes.push_back(uniquename.str()); -} - -void FuncGen::popLabelScope() -{ - labelScopes.pop_back(); - nextUnique.pop(); -} - -void FuncGen::pushToElemScope() -{ - toElemScopes.push(static_cast(temporariesToDestruct.size())); -} - -void FuncGen::popToElemScope(bool destructTemporaries) -{ - assert(!toElemScopes.empty()); - - const bool isOuterMost = (toElemScopes.size() == 1); - if (destructTemporaries || isOuterMost) - { - int numInitialTemporaries = toElemScopes.back(); - assert(!isOuterMost || numInitialTemporaries == 0); - this->destructTemporaries(numInitialTemporaries); - } - - toElemScopes.pop(); -} - -void FuncGen::pushTemporaryToDestruct(VarDeclaration* vd) -{ - temporariesToDestruct.push(vd); -} - -bool FuncGen::hasTemporariesToDestruct() -{ - return !temporariesToDestruct.empty(); -} - -VarDeclarations& FuncGen::getTemporariesToDestruct() -{ - return temporariesToDestruct; -} - -void FuncGen::destructTemporaries(unsigned numToKeep) -{ - // pop one temporary after the other from the temporariesToDestruct stack - // and evaluate its destructor expression - // so when an exception occurs in a destructor expression, all older - // temporaries (excl. the one which threw in its destructor) will be - // destructed in a landing pad - while (temporariesToDestruct.size() > numToKeep) - { - VarDeclaration* vd = temporariesToDestruct.pop(); - toElemDtor(vd->edtor); - } -} - -void FuncGen::destructAllTemporariesAndRestoreStack() -{ - VarDeclarations original = temporariesToDestruct; - destructTemporaries(0); - temporariesToDestruct = original; -} - -void FuncGen::prepareToDestructAllTemporariesOnThrow(IRState* irState) -{ - class CallDestructors : public IRLandingPadCatchFinallyInfo - { - public: - FuncGen& funcGen; - CallDestructors(FuncGen& funcGen) : funcGen(funcGen) {} - void toIR(LLValue*) - { - funcGen.destructAllTemporariesAndRestoreStack(); + // Set up the unconditional branch at the end of the cleanup if we have + // not done so already. + if (scope.exitTargets.empty()) { + scope.exitTargets.push_back(CleanupExitTarget(continueWith)); + llvm::BranchInst::Create(continueWith, scope.endBlock); } - }; + scope.exitTargets.front().sourceBlocks.push_back(sourceBlock); + return; + } - CallDestructors* callDestructors = new CallDestructors(*this); + // We need a branch selector if we are here... + if (!scope.branchSelector) { + // ... and have not created one yet, so do so now. + scope.branchSelector = new llvm::AllocaInst( + llvm::Type::getInt32Ty(gIR->context()), + llvm::Twine("branchsel.") + scope.beginBlock->getName(), + irs->topallocapoint() + ); - // create landing pad - llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(irState->context(), - "temporariesLandingPad", irState->topfunc()); + // Now we also need to store 0 to it to keep the paths that go to the + // only existing branch target the same. + std::vector& v = scope.exitTargets.front().sourceBlocks; + for (std::vector::iterator it = v.begin(), end = v.end(); + it != end; ++it + ) { + new llvm::StoreInst(DtoConstUint(0), scope.branchSelector, + (*it)->getTerminator()); + } - // set up the landing pad - landingPadInfo.addFinally(callDestructors, /* deleteOnPop = */ true); - landingPadInfo.push(landingpadbb); + // And convert the BranchInst to the existing branch target to a + // SelectInst so we can append the other cases to it. + scope.endBlock->getTerminator()->eraseFromParent(); + llvm::Value *sel = new llvm::LoadInst(scope.branchSelector, "", + scope.endBlock); + llvm::SwitchInst::Create( + sel, + scope.exitTargets[0].branchTarget, + 1, // Expected number of branches, only for pre-allocating. + scope.endBlock + ); + } + + // If we already know this branch target, figure out the branch selector + // value and simply insert the store into the source block (prior to the + // last instruction, which is the branch to the first cleanup). + for (unsigned i = 0; i < scope.exitTargets.size(); ++i) { + CleanupExitTarget& t = scope.exitTargets[i]; + if (t.branchTarget == continueWith) { + new llvm::StoreInst(DtoConstUint(i), scope.branchSelector, + sourceBlock->getTerminator()); + + // Note: Strictly speaking, keeping this up to date would not be + // needed right now, because we never to any optimizations that + // require changes to the source blocks after the initial conversion + // from one to two branch targets. Keeping this around for now to + // ease future development, but may be removed to save some work. + t.sourceBlocks.push_back(sourceBlock); + + return; + } + } + + // We don't know this branch target yet, so add it to the SwitchInst... + llvm::ConstantInt * const selectorVal = DtoConstUint(scope.exitTargets.size()); + llvm::cast(scope.endBlock->getTerminator())->addCase( + selectorVal, continueWith); + + // ... insert the store into the source block... + new llvm::StoreInst(selectorVal, scope.branchSelector, + sourceBlock->getTerminator()); + + // ... and keep track of it (again, this is unnecessary right now as + // discussed in the above note). + scope.exitTargets.push_back(CleanupExitTarget(continueWith)); + scope.exitTargets.back().sourceBlocks.push_back(sourceBlock); +} } +ScopeStack::~ScopeStack() { + if (!topLevelUnresolvedGotos.empty()) { + for (std::vector::iterator it = topLevelUnresolvedGotos.begin(), + end = topLevelUnresolvedGotos.end(); + it != end; ++it + ) { + error(it->sourceLoc, "goto into try/finally scope is not allowed"); + } + fatal(); + } +} + +void ScopeStack::pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) { + cleanupScopes.push_back(CleanupScope(beginBlock, endBlock)); +} + +void ScopeStack::runCleanups(CleanupCursor targetScope, + llvm::BasicBlock* continueWith +) { + assert(targetScope <= currentCleanupScope()); + + if (targetScope == currentCleanupScope()) { + // No cleanups to run, just branch to the next block. + llvm::BranchInst::Create(continueWith, irs->scopebb()); + return; + } + + // Insert the unconditional branch to the first cleanup block. + irs->ir->CreateBr(cleanupScopes.back().beginBlock); + + // Update all the control flow in the cleanups to make sure we end up where + // we want. + for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { + llvm::BasicBlock *nextBlock = (i > targetScope) ? + cleanupScopes[i - 1].beginBlock : continueWith; + executeCleanup(irs, cleanupScopes[i], irs->scopebb(), nextBlock); + } +} + +void ScopeStack::runAllCleanups(llvm::BasicBlock* continueWith) { + runCleanups(0, continueWith); +} + +void ScopeStack::popCleanups(CleanupCursor targetScope) { + if (targetScope == currentCleanupScope()) return; + + for (CleanupCursor i = currentCleanupScope(); i-- > targetScope;) { + for (std::vector::iterator it = currentUnresolvedGotos().begin(), + end = currentUnresolvedGotos().end(); + it != end; ++it + ) { + // Make the source resp. last cleanup branch to this one. + llvm::BasicBlock *tentative = it->tentativeTarget; + tentative->replaceAllUsesWith(cleanupScopes[i].beginBlock); + + // And continue execution with the tentative target (we simply reuse + // it because there is no reason not to). + executeCleanup(irs, cleanupScopes[i], it->sourceBlock, tentative); + } + + + std::vector& nextUnresolved = (i == 0) ? + topLevelUnresolvedGotos : cleanupScopes[i - 1].unresolvedGotos; + nextUnresolved.insert( + nextUnresolved.end(), + currentUnresolvedGotos().begin(), + currentUnresolvedGotos().end() + ); + + cleanupScopes.pop_back(); + } +} + +void ScopeStack::pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget, + llvm::BasicBlock* breakTarget +) { + continueTargets.push_back({continueTarget, currentCleanupScope(), loopStatement}); + breakTargets.push_back({breakTarget, currentCleanupScope(), loopStatement}); +} + +void ScopeStack::popLoopTarget() { + continueTargets.pop_back(); + breakTargets.pop_back(); +} + +void ScopeStack::pushBreakTarget(Statement* switchStatement, + llvm::BasicBlock* targetBlock +) { + breakTargets.push_back({targetBlock, currentCleanupScope(), switchStatement}); +} + +void ScopeStack::popBreakTarget() { + breakTargets.pop_back(); +} + +void ScopeStack::addLabelTarget(Identifier* labelName, + llvm::BasicBlock* targetBlock +) { + labelTargets[labelName] = {targetBlock, currentCleanupScope(), 0}; + + std::vector& unresolved = currentUnresolvedGotos(); + size_t i = 0; + while (i < unresolved.size()) { + if (unresolved[i].targetLabel != labelName) { + ++i; + continue; + } + + unresolved[i].tentativeTarget->replaceAllUsesWith(targetBlock); + unresolved[i].tentativeTarget->eraseFromParent(); + unresolved.erase(unresolved.begin() + i); + } +} + +void ScopeStack::jumpToLabel(Loc loc, Identifier* labelName) { + // If we have already seen that label, branch to it, executing any cleanups + // as necessary. + LabelTargetMap::iterator it = labelTargets.find(labelName); + if (it != labelTargets.end()) { + runCleanups(it->second.cleanupScope, it->second.targetBlock); + return; + } + + llvm::BasicBlock *target = + llvm::BasicBlock::Create(irs->context(), "goto.unresolved", irs->topfunc()); + irs->ir->CreateBr(target); + currentUnresolvedGotos().push_back({loc, irs->scopebb(), target, labelName}); +} + +void ScopeStack::jumpToStatement(std::vector& targets, + Statement* loopOrSwitchStatement +) { + for (std::vector::reverse_iterator it = targets.rbegin(), + end = targets.rend(); + it != end; ++it + ) { + if (it->targetStatement == loopOrSwitchStatement) { + runCleanups(it->cleanupScope, it->targetBlock); + return; + } + } + assert(false && "Target for labeled break not found."); +} + +void ScopeStack::jumpToClosest(std::vector& targets) { + assert(!targets.empty() && + "Encountered break/continue but no loop in scope."); + JumpTarget &t = targets.back(); + runCleanups(t.cleanupScope, t.targetBlock); +} + +std::vector& ScopeStack::currentUnresolvedGotos() { + return cleanupScopes.empty() ? + topLevelUnresolvedGotos : + cleanupScopes.back().unresolvedGotos; +} IrFunction::IrFunction(FuncDeclaration* fd) { @@ -151,6 +285,9 @@ IrFunction::IrFunction(FuncDeclaration* fd) _arguments = NULL; _argptr = NULL; + + retValSlot = NULL; + retBlock = NULL; } void IrFunction::setNeverInline() diff --git a/ir/irfunction.h b/ir/irfunction.h index 9917224a6c..ea359c4463 100644 --- a/ir/irfunction.h +++ b/ir/irfunction.h @@ -19,97 +19,240 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "gen/llvm.h" -#include "ir/irlandingpad.h" #include "ir/irfuncty.h" #include #include #include +class Identifier; class Statement; -struct EnclosingTryFinally; struct IRState; -// scope statements that can be target of jumps -// includes loops, switch, case, labels -struct IRTargetScope -{ - // generating statement - Statement* s; +/// Represents a position on the stack of currently active cleanup scopes. +/// +/// Since we always need to run a contiguous part of the stack (or all) in +/// order, this is enough to uniquely identify the location of a given target. +typedef size_t CleanupCursor; - // the try-finally block that encloses the statement - EnclosingTryFinally* enclosinghandler; +/// Stores information needed to correctly jump to a given label or loop +/// statement (break/continue). +struct JumpTarget { + /// The basic block to ultimately branch to. + llvm::BasicBlock* targetBlock; - llvm::BasicBlock* breakTarget; - llvm::BasicBlock* continueTarget; + /// The index of the label target in the stack of active cleanup scopes. + /// + /// When generating code for a jump to this label, the cleanups between + /// the current depth and that of the level will be emitted. Note that + /// we need to handle only one direction (towards the root of the stack) + /// because D forbids gotos into try or finally blocks. + // TODO: We might not be able to detect illegal jumps across try-finally + // blocks by only storing the index. + CleanupCursor cleanupScope; - /// If true, the breakTarget is only considered if it is explicitly - /// specified (via s), and not for unqualified "break;" statements. - bool onlyLabeledBreak; - - IRTargetScope( - Statement* s, - EnclosingTryFinally* enclosinghandler, - llvm::BasicBlock* continueTarget, - llvm::BasicBlock* breakTarget, - bool onlyLabeledBreak = false - ); + /// Keeps target of the associated loop or switch statement so we can + /// handle both unlabeled and labeled jumps. + Statement* targetStatement; }; -struct FuncGen -{ - FuncGen(); +/// Defines source and target label of a goto (used if we cannot immediately +/// figure out the target basic block). +struct GotoJump { + // The location of the jump instruction, for error reporting. + Loc sourceLoc; - // pushes a unique label scope of the given name - void pushUniqueLabelScope(const char* name); - // pops a label scope - void popLabelScope(); + /// The basic block which contains the goto as its terminator. + llvm::BasicBlock* sourceBlock; - // gets the string under which the label's BB - // is stored in the labelToBB map. - // essentially prefixes ident by the strings in labelScopes - std::string getScopedLabelName(const char* ident); + /// While we have not found the actual branch target, we might need to + /// create a "fake" basic block in order to be able to execute the cleanups + /// (we do not keep branching information around after leaving the scope). + llvm::BasicBlock* tentativeTarget; - // label to basic block lookup - typedef std::map LabelToBBMap; - LabelToBBMap labelToBB; + /// The label to target with the goto. + Identifier* targetLabel; +}; - // loop blocks - typedef std::vector TargetScopeVec; - TargetScopeVec targetScopes; +/// Describes a particular way to leave a certain scope and continue execution +/// at another one (return, break/continue, exception handling, etc.). +struct CleanupExitTarget { + explicit CleanupExitTarget(llvm::BasicBlock* t) : branchTarget(t) {} - // landing pads for try statements - IRLandingPad landingPadInfo; - llvm::BasicBlock* landingPad; + /// The target basic block to branch to after running the cleanup. + llvm::BasicBlock* branchTarget; - void pushToElemScope(); - void popToElemScope(bool destructTemporaries); + /// The basic blocks that want to continue with this target after running + /// the cleanup. We need to keep this information around so we can insert + /// stores to the branch selector variable when converting from one to two + /// targets. + std::vector sourceBlocks; +}; - void pushTemporaryToDestruct(VarDeclaration* vd); - bool hasTemporariesToDestruct(); - VarDeclarations& getTemporariesToDestruct(); +/// Represents a scope (in abstract terms, not curly braces) that requires a +/// piece of cleanup code to be run whenever it is left, whether as part of +/// normal control flow or exception unwinding. +/// +/// This includes finally blocks (which are also generated by the frontend for +/// running the destructors of non-temporary variables) and the destructors of +/// temporaries (which are unfortunately not lowered by the frontend). +/// +/// Our goal is to only emit each cleanup once such as to avoid generating an +/// exponential number of basic blocks/landing pads for handling all the +/// different ways of exiting a deeply nested scope (exception/no exception/...). +class CleanupScope { +public: + CleanupScope(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock) : + beginBlock(beginBlock), endBlock(endBlock), branchSelector(0) {} - void destructAllTemporariesAndRestoreStack(); - // pushes a landing pad which needs to be popped after the - // following invoke instruction - void prepareToDestructAllTemporariesOnThrow(IRState* irState); + /// The basic block to branch to for running the cleanup. + llvm::BasicBlock* beginBlock; + + /// The basic block that contains the end of the cleanuip code (is different + /// from beginBlock if the cleanup contains control flow). + llvm::BasicBlock* endBlock; + + /// The branch selector variable, or null if not created yet. + llvm::AllocaInst* branchSelector; + + /// Stores all possible targets blocks after running this cleanup, along + /// with what predecessors want to continue at that target. The index in + /// the vector corresponds to the branch selector value for that target. + std::vector exitTargets; + + /// Keeps track of all the gotos somewhere inside this scope for which we + /// have not found the label yet (because it occurs lexically later in the + /// function). + std::vector unresolvedGotos; +}; + +/// Contains transitory information about the current scope, etc. while +/// traversing the function for codegen purposes. +class ScopeStack { +public: + ScopeStack(IRState *irs) : irs(irs) {} + ~ScopeStack(); + + /// Registers a piece of cleanup code to be run. + /// + /// The basic block is expected not to contain a terminator yet. It will be + /// added by ScopeStack as needed based on what followup blocks there will be + /// registered. + void pushCleanup(llvm::BasicBlock* beginBlock, llvm::BasicBlock* endBlock); + + /// Terminates the current IRScope with a branch to the cleanups needed for + /// leaving the given scope and continuing execution at the target scope + /// stack level. + /// + /// After running them, execution will branch to the given basic block. + void runCleanups(CleanupCursor targetScope, llvm::BasicBlock* continueWith); + + /// Like #runCleanups(), but runs all of them. + void runAllCleanups(llvm::BasicBlock* continueWith); + + /// Pops all the cleanups between the current scope and the target cursor. + void popCleanups(CleanupCursor targetScope); + + /// Returns a cursor that identifies the curernt cleanup scope, to be later + /// userd with #popCleanups() et al. + /// + /// Note that this cursor is only valid as long as the current scope is not + /// popped. + CleanupCursor currentCleanupScope() { return cleanupScopes.size(); } + +#if 0 + void suspendCleanup(CleanupCursor target); + + void resumeCleanup(CleanupCursor target); +#endif + + /// + void pushCatch(llvm::Value* classInfoPtr, llvm::Value* exceptionVar, + llvm::BasicBlock* bodyBlock) {} + + /// + void popCatch() {} + + /// + void pushLoopTarget(Statement* loopStatement, llvm::BasicBlock* continueTarget, + llvm::BasicBlock* breakTarget); + + /// Pops the last pushed loop target, so it is no longer taken into + /// consideration for resolving breaks/continues. + void popLoopTarget(); + + /// + void pushBreakTarget(Statement* switchStatement, llvm::BasicBlock* targetBlock); + + /// + void popBreakTarget(); + + /// + void addLabelTarget(Identifier* labelName, llvm::BasicBlock* targetBlock); + + /// + void jumpToLabel(Loc loc, Identifier* labelName); + + /// + void continueWithLoop(Statement* loopStatement) { + jumpToStatement(continueTargets, loopStatement); + } + + /// + void continueWithClosest() { + jumpToClosest(continueTargets); + } + + /// + void breakToStatement(Statement* loopOrSwitchStatement) { + jumpToStatement(breakTargets, loopOrSwitchStatement); + } + + /// + void breakToClosest() { + jumpToClosest(breakTargets); + } private: - // prefix for labels and gotos - // used for allowing labels to be emitted twice - std::vector labelScopes; + std::vector& currentUnresolvedGotos(); - // next unique id stack - std::stack nextUnique; + void jumpToStatement(std::vector& targets, Statement* loopOrSwitchStatement); - Array toElemScopes; // number of initial temporaries - VarDeclarations temporariesToDestruct; + void jumpToClosest(std::vector& targets); - void destructTemporaries(unsigned numToKeep); + IRState *irs; + + typedef llvm::DenseMap LabelTargetMap; + /// The labels we have encountered in this function so far, accessed by + /// their associated identifier (i.e. the name of the label). + LabelTargetMap labelTargets; + + /// + std::vector breakTargets; + + /// + std::vector continueTargets; + + /// + std::vector cleanupScopes; + + /// Gotos which we were not able to resolve to any cleanup scope, but which + /// might still be defined later in the function at top level. If there are + /// any left on function exit, it is an error (e.g. because the user tried + /// to goto into a finally block, etc.). + std::vector topLevelUnresolvedGotos; + +#if 0 + /// To be able to handle the broken AST the frontend produces in some cases + /// for temporary constructor calls, we need to be able to temporarily + /// suspend a cleanup from being run without removing it from the stack. + // FIXME: Make sure this does not break our usual assumption that all + // temporaries are run all the time. + std::vector suspendedCleanups; +#endif }; // represents a function -struct IrFunction -{ +struct IrFunction { // constructor IrFunction(FuncDeclaration* fd); @@ -122,7 +265,7 @@ struct IrFunction FuncDeclaration* decl; TypeFunction* type; - FuncGen* gen; + ScopeStack* scopes; bool queued; bool defined; @@ -141,6 +284,11 @@ struct IrFunction llvm::Value* _arguments; llvm::Value* _argptr; + // A stack slot containing the return value, for functions that return by value. + llvm::AllocaInst* retValSlot; + // The basic block with the return instruction. + llvm::BasicBlock* retBlock; + #if LDC_LLVM_VER >= 307 llvm::DISubprogram* diSubprogram = nullptr; std::stack diLexicalBlocks; diff --git a/ir/irlandingpad.cpp b/ir/irlandingpad.cpp deleted file mode 100644 index 2582b18323..0000000000 --- a/ir/irlandingpad.cpp +++ /dev/null @@ -1,253 +0,0 @@ -//===-- irlandingpad.cpp --------------------------------------------------===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. -// -//===----------------------------------------------------------------------===// - -#include "gen/llvm.h" -#include "gen/classes.h" -#include "gen/irstate.h" -#include "gen/llvmhelpers.h" -#include "gen/logger.h" -#include "gen/runtime.h" -#include "gen/tollvm.h" -#include "ir/irlandingpad.h" - -// creates new landing pad -static llvm::LandingPadInst *createLandingPadInst() -{ - LLType* retType = LLStructType::get(LLType::getInt8PtrTy(gIR->context()), - LLType::getInt32Ty(gIR->context()), - NULL); -#if LDC_LLVM_VER >= 307 - LLFunction* currentFunction = gIR->func()->func; - if (!currentFunction->hasPersonalityFn()) { - LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_personality"); - currentFunction->setPersonalityFn(personalityFn); - } - return gIR->ir->CreateLandingPad(retType, 0, "landing_pad"); -#else - LLFunction* personalityFn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_personality"); - return gIR->ir->CreateLandingPad(retType, personalityFn, 0, "landing_pad"); -#endif -} - -IRLandingPadCatchInfo::IRLandingPadCatchInfo(Catch* catchstmt_, llvm::BasicBlock* end_) : - end(end_), catchStmt(catchstmt_) -{ - target = llvm::BasicBlock::Create(gIR->context(), "catch", gIR->topfunc(), end); - - assert(catchStmt->type); - catchType = catchStmt->type->toBasetype()->isClassHandle(); - assert(catchType); - DtoResolveClass(catchType); - - if (catchStmt->var) { - if (!catchStmt->var->nestedrefs.dim) { - gIR->func()->gen->landingPadInfo.getExceptionStorage(); - } - } -} - -void IRLandingPadCatchInfo::toIR() -{ - if (!catchStmt) - return; - - gIR->scope() = IRScope(target); - gIR->DBuilder.EmitBlockStart(catchStmt->loc); - - LLFunction* enterCatchFn = - LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_enter_catch"); - gIR->ir->CreateCall(enterCatchFn); - - // assign storage to catch var - if (catchStmt->var) { - LLType* llCatchVarType = DtoType(catchStmt->var->type); // e.g., Throwable* - LLValue* catch_var = gIR->func()->gen->landingPadInfo.getExceptionStorage(); - - // use the same storage for all exceptions that are not accessed in - // nested functions - if (!catchStmt->var->nestedrefs.dim) { - assert(!isIrLocalCreated(catchStmt->var)); - IrLocal* irLocal = getIrLocal(catchStmt->var, true); - irLocal->value = DtoBitCast(catch_var, getPtrToType(llCatchVarType)); - } else { - // this will alloca if we haven't already and take care of nested refs - DtoDeclarationExp(catchStmt->var); - IrLocal* irLocal = getIrLocal(catchStmt->var); - - // the exception is stored in catch_var. copy it over if necessary - LLValue* exc = DtoBitCast(DtoLoad(catch_var), llCatchVarType); - DtoStore(exc, irLocal->value); - } - } - - // emit handler, if there is one - // handler is zero for instance for 'catch { debug foo(); }' - if (catchStmt->handler) - Statement_toIR(catchStmt->handler, gIR); - - if (!gIR->scopereturned()) - gIR->ir->CreateBr(end); - - gIR->DBuilder.EmitBlockEnd(); -} - -IRLandingPadFinallyStatementInfo::IRLandingPadFinallyStatementInfo(Statement *finallyBody_) : - finallyBody(finallyBody_) -{ -} - -void IRLandingPadFinallyStatementInfo::toIR(LLValue *eh_ptr) -{ - IRLandingPad &padInfo = gIR->func()->gen->landingPadInfo; - llvm::BasicBlock* &pad = gIR->func()->gen->landingPad; - - gIR->DBuilder.EmitBlockStart(finallyBody->loc); - Statement_toIR(finallyBody, gIR); - gIR->DBuilder.EmitBlockEnd(); -} - - -void IRLandingPad::addCatch(Catch* catchstmt, llvm::BasicBlock* end) -{ - unpushedScope.catches.push_back(IRLandingPadCatchInfo(catchstmt, end)); -} - -void IRLandingPad::addFinally(Statement* finallyStmt) -{ - addFinally(new IRLandingPadFinallyStatementInfo(finallyStmt), true); -} - -void IRLandingPad::addFinally(IRLandingPadCatchFinallyInfo *finallyInfo, - bool deleteOnPop) -{ - assert(unpushedScope.finally == NULL && "only one finally per try-finally block"); - unpushedScope.finally = finallyInfo; - unpushedScope.isFinallyCreatedInternally = deleteOnPop; -} - -void IRLandingPad::push(llvm::BasicBlock* inBB) -{ - unpushedScope.target = inBB; - scopeStack.push(unpushedScope); - unpushedScope = IRLandingPadScope(); - gIR->func()->gen->landingPad = get(); -} - -void IRLandingPad::pop() -{ - IRLandingPadScope scope = scopeStack.top(); - scopeStack.pop(); - gIR->func()->gen->landingPad = get(); - - std::deque::iterator itr, end = scope.catches.end(); - for (itr = scope.catches.begin(); itr != end; ++itr) - itr->toIR(); - constructLandingPad(scope); - if (scope.finally && scope.isFinallyCreatedInternally) - delete scope.finally; -} - -llvm::BasicBlock* IRLandingPad::get() -{ - if (scopeStack.size() == 0) - return NULL; - else - return scopeStack.top().target; -} - -void IRLandingPad::constructLandingPad(IRLandingPadScope scope) -{ - // save and rewrite scope - IRScope savedIRScope = gIR->scope(); - gIR->scope() = IRScope(scope.target); - - // create landingpad - 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); - - bool isFirstCatch = true; - std::stack savedScopeStack = scopeStack; - std::deque::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 LDC_LLVM_VER >= 305 - if (global.params.targetTriple.isWindowsMSVCEnvironment()) - { - // eh_ptr is a pointer to the Throwable object. - LLType *objectTy = DtoType(ClassDeclaration::object->type); - LLValue *object = gIR->ir->CreateBitCast(eh_ptr, objectTy); - gIR->ir->CreateStore(object, catch_var); - } - else -#endif - { - // 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()); - LLValue *objectPtr = gIR->ir->CreateBitCast(eh_ptr, objectPtrTy); - gIR->ir->CreateStore(gIR->ir->CreateLoad(objectPtr), catch_var); - } - isFirstCatch = false; - } - - // create next block - llvm::BasicBlock *next = llvm::BasicBlock::Create(gIR->context(), "eh.next", gIR->topfunc()); - // get class info symbol - LLValue *classInfo = getIrAggr(catchItr->catchType)->getClassInfoSymbol(); - // add that symbol as landing pad clause - landingPad->addClause(llvm::cast(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), catchItr->target, next); - gIR->scope() = IRScope(next); - } - - if (scope.finally) { - scope.finally->toIR(eh_ptr); - landingPad->setCleanup(true); - } - - if (scopeStack.empty()) - break; - scope = scopeStack.top(); - scopeStack.pop(); - gIR->func()->gen->landingPad = get(); - } - - // restore landing pad infos - scopeStack = savedScopeStack; - gIR->func()->gen->landingPad = get(); - - // no catch matched and all finallys executed - resume unwind - llvm::Function* unwind_resume_fn = LLVM_D_GetRuntimeFunction(Loc(), gIR->module, "_d_eh_resume_unwind"); - gIR->ir->CreateCall(unwind_resume_fn, eh_ptr); - gIR->ir->CreateUnreachable(); - - // restore scope - gIR->scope() = savedIRScope; -} - -LLValue* IRLandingPad::getExceptionStorage() -{ - if (!catch_var) { - Logger::println("Making new catch var"); - catch_var = DtoAlloca(ClassDeclaration::object->type, "catchvar"); - } - return catch_var; -} diff --git a/ir/irlandingpad.h b/ir/irlandingpad.h deleted file mode 100644 index a61db3b8de..0000000000 --- a/ir/irlandingpad.h +++ /dev/null @@ -1,125 +0,0 @@ -//===-- ir/irlandingpad.h - Codegen state for EH blocks ---------*- C++ -*-===// -// -// LDC – the LLVM D compiler -// -// This file is distributed under the BSD-style LDC license. See the LICENSE -// file for details. -// -//===----------------------------------------------------------------------===// -// -// State kept while doing codegen for a single "EH block" consisting of -// of several catch/finally/cleanup clauses. Handles nesting of these blocks. -// -//===----------------------------------------------------------------------===// - -#ifndef LDC_IR_IRLANDINGPADINFO_H -#define LDC_IR_IRLANDINGPADINFO_H - -#include "statement.h" -#include -#include - -namespace llvm { - class Type; - class Value; - class BasicBlock; - class Function; -} - -// holds information about a single catch -struct IRLandingPadCatchInfo -{ - // default constructor for being able to store in a vector - IRLandingPadCatchInfo() : - target(NULL), end(0), catchStmt(NULL), catchType(0) - {} - - IRLandingPadCatchInfo(Catch* catchStmt, llvm::BasicBlock* end); - - // codegen the catch block - void toIR(); - - llvm::BasicBlock *target; - llvm::BasicBlock *end; - Catch *catchStmt; - ClassDeclaration *catchType; -}; - -// holds information about a single finally -class IRLandingPadCatchFinallyInfo -{ -public: - virtual ~IRLandingPadCatchFinallyInfo() {} - virtual void toIR(LLValue *eh_ptr) = 0; -}; - -class IRLandingPadFinallyStatementInfo : public IRLandingPadCatchFinallyInfo -{ -public: - IRLandingPadFinallyStatementInfo(Statement *finallyBody); - // codegen the finally block - void toIR(LLValue *eh_ptr); -private: - // the body of finally - Statement *finallyBody; -}; - -// holds information about a single try-catch-finally block -struct IRLandingPadScope -{ - explicit IRLandingPadScope(llvm::BasicBlock *target_ = NULL) : - target(target_), finally(0), isFinallyCreatedInternally(false) {} - - // the target for invokes - llvm::BasicBlock *target; - // information about catch blocks - std::deque catches; - // information about a finally block - IRLandingPadCatchFinallyInfo *finally; - bool isFinallyCreatedInternally; -}; - - -// holds information about all possible catch and finally actions -// and can emit landing pads to be called from the unwind runtime -struct IRLandingPad -{ - IRLandingPad() : catch_var(NULL) {} - - // creates 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); - - // add catch information, will be used in next call to push - void addCatch(Catch* catchstmt, llvm::BasicBlock* end); - // add finally statement, will be used in next call to push - void addFinally(Statement* finallyStmt); - // add finally information, will be used in next call to push - void addFinally(IRLandingPadCatchFinallyInfo *finallyInfo, - bool deleteOnPop = false); - - // builds the most recently constructed landing pad - // and the catch blocks, then pops the landing pad bb - // and its infos - void pop(); - - // creates or gets storage for exception object - llvm::Value* getExceptionStorage(); - -private: - friend class IRLandingPadFinallyStatementInfo; - // gets the current landing pad - llvm::BasicBlock* get(); - - // constructs the landing pad - void constructLandingPad(IRLandingPadScope scope); - - // information about try-catch-finally blocks - std::stack scopeStack; - IRLandingPadScope unpushedScope; - - // storage for the catch variable - llvm::Value* catch_var; -}; - -#endif