From 4236ae9ce5891c40e7b121d032ec60162a85fa05 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 19 Aug 2015 00:56:33 +0200 Subject: [PATCH] The big catch/finally rework, part 1 Never generates any landing pads or invoke instructions right now for simplicity. The code for emitting them will be added back in the next step. The "after..." blocks without any precedessors remain for now, as we need a clean way to suppress any codegen for that block (but not new blocks, which might resolve labels) before tackling that one. Builds druntime/Phobos on OS X x86_64 (albeit without EH, of course). --- gen/asmstmt.cpp | 2 +- gen/functions.cpp | 63 +-- gen/irstate.cpp | 17 - gen/irstate.h | 11 +- gen/llvmhelpers.cpp | 85 +--- gen/llvmhelpers.h | 17 +- gen/statements.cpp | 932 ++++++++++++++++---------------------------- gen/toir.cpp | 52 ++- ir/irfunction.cpp | 345 +++++++++++----- ir/irfunction.h | 274 ++++++++++--- ir/irlandingpad.cpp | 253 ------------ ir/irlandingpad.h | 125 ------ 12 files changed, 861 insertions(+), 1315 deletions(-) delete mode 100644 ir/irlandingpad.cpp delete mode 100644 ir/irlandingpad.h 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