// Statements: D -> LLVM glue #include #include #include #include "gen/llvm.h" #include "llvm/InlineAsm.h" #include "llvm/Support/CFG.h" #include "mars.h" #include "init.h" #include "mtype.h" #include "hdrgen.h" #include "port.h" #include "module.h" #include "gen/irstate.h" #include "gen/logger.h" #include "gen/tollvm.h" #include "gen/llvmhelpers.h" #include "gen/runtime.h" #include "gen/arrays.h" #include "gen/todebug.h" #include "gen/dvalue.h" #include "gen/abi.h" #include "ir/irfunction.h" #include "ir/irmodule.h" #include "ir/irlandingpad.h" ////////////////////////////////////////////////////////////////////////////// void CompoundStatement::toIR(IRState* p) { Logger::println("CompoundStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; for (unsigned i=0; idim; i++) { Statement* s = static_cast(statements->data[i]); if (s) { s->toIR(p); } } } ////////////////////////////////////////////////////////////////////////////// void ReturnStatement::toIR(IRState* p) { Logger::println("ReturnStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); // is there a return value expression? if (exp || (!exp && (p->topfunc() == p->mainFunc)) ) { // if the functions return type is void this means that // we are returning through a pointer argument if (p->topfunc()->getReturnType() == LLType::getVoidTy(gIR->context())) { // sanity check IrFunction* f = p->func(); assert(f->decl->ir.irFunc->retArg); // FIXME: is there ever a case where a sret return needs to be rewritten for the ABI? // get return pointer DValue* rvar = new DVarValue(f->type->next, f->decl->ir.irFunc->retArg); DValue* e = exp->toElemDtor(p); // store return value DtoAssign(loc, rvar, e); #if DMDV2 // call postblit if necessary if (!p->func()->type->isref && !(f->decl->nrvo_can && f->decl->nrvo_var)) callPostblit(loc, exp, rvar->getLVal()); #endif // emit scopes DtoEnclosingHandlers(loc, NULL); // emit dbg end function DtoDwarfFuncEnd(f->decl); // emit ret llvm::ReturnInst::Create(gIR->context(), p->scopebb()); } // the return type is not void, so this is a normal "register" return else { LLValue* v = 0; if (!exp && (p->topfunc() == p->mainFunc)) { v = LLConstant::getNullValue(p->mainFunc->getReturnType()); } else { if (exp->op == TOKnull) exp->type = p->func()->type->next; #if DMDV2 DValue* dval = 0; // call postblit if necessary if (!p->func()->type->isref) { dval = exp->toElemDtor(p); callPostblit(loc, exp, dval->getRVal()); } else { Expression *ae = exp->addressOf(NULL); dval = ae->toElemDtor(p); } // do abi specific transformations on the return value v = p->func()->type->fty.putRet(exp->type, dval); #else DValue* dval = exp->toElemDtor(p); v = p->func()->type->fty.putRet(exp->type, dval); #endif } if (Logger::enabled()) Logger::cout() << "return value is '" <<*v << "'\n"; IrFunction* f = p->func(); // Hack around LDC assuming structs and static arrays are in memory: // If the function returns a struct or a static array, and the return // 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() != p->topfunc()->getReturnType() && (ty == Tstruct #if DMDV2 || ty == Tsarray #endif ) && isaPointer(v->getType())) { Logger::println("Loading value for return"); v = DtoLoad(v); } // can happen for classes and void main if (v->getType() != p->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 // return type remains i32, we just throw away the exp value // and return 0 instead // if we're not in main, just bitcast if (p->topfunc() == p->mainFunc) v = LLConstant::getNullValue(p->mainFunc->getReturnType()); else v = gIR->ir->CreateBitCast(v, p->topfunc()->getReturnType(), "tmp"); if (Logger::enabled()) Logger::cout() << "return value after cast: " << *v << '\n'; } // emit scopes DtoEnclosingHandlers(loc, NULL); DtoDwarfFuncEnd(p->func()->decl); llvm::ReturnInst::Create(gIR->context(), v, p->scopebb()); } } // no return value expression means it's a void function else { assert(p->topfunc()->getReturnType() == LLType::getVoidTy(gIR->context())); DtoEnclosingHandlers(loc, NULL); DtoDwarfFuncEnd(p->func()->decl); llvm::ReturnInst::Create(gIR->context(), p->scopebb()); } // the return terminated this basicblock, start a new one llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterreturn", p->topfunc(), oldend); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void ExpStatement::toIR(IRState* p) { Logger::println("ExpStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); if (exp) { if (global.params.llvmAnnotate) DtoAnnotation(exp->toChars()); elem* e; // a cast(void) around the expression is allowed, but doesn't require any code if(exp->op == TOKcast && exp->type == Type::tvoid) { CastExp* cexp = static_cast(exp); e = cexp->e1->toElemDtor(p); } else e = exp->toElemDtor(p); delete e; } /*elem* e = exp->toElem(p); p->buf.printf("%s", e->toChars()); delete e; p->buf.writenl();*/ } ////////////////////////////////////////////////////////////////////////////// #if DMDV2 void DtorExpStatement::toIR(IRState *irs) { assert(irs->func()); FuncDeclaration *fd = irs->func()->decl; assert(fd); if (fd->nrvo_can && fd->nrvo_var == var) /* Do not call destructor, because var is returned as the nrvo variable. * This is done at this stage because nrvo can be turned off at a * very late stage in semantic analysis. */ ; else { ExpStatement::toIR(irs); } } #endif ////////////////////////////////////////////////////////////////////////////// void IfStatement::toIR(IRState* p) { Logger::println("IfStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start a dwarf lexical block DtoDwarfBlockStart(loc); if (match) DtoRawVarDeclaration(match); DValue* cond_e = condition->toElemDtor(p); LLValue* cond_val = cond_e->getRVal(); llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* ifbb = llvm::BasicBlock::Create(gIR->context(), "if", gIR->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endif", gIR->topfunc(), oldend); llvm::BasicBlock* elsebb = elsebody ? llvm::BasicBlock::Create(gIR->context(), "else", gIR->topfunc(), endbb) : endbb; if (cond_val->getType() != LLType::getInt1Ty(gIR->context())) { if (Logger::enabled()) Logger::cout() << "if conditional: " << *cond_val << '\n'; cond_val = DtoCast(loc, cond_e, Type::tbool)->getRVal(); } llvm::BranchInst::Create(ifbb, elsebb, cond_val, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(ifbb,elsebb); // do scoped statements if (ifbody) { DtoDwarfBlockStart(ifbody->loc); ifbody->toIR(p); DtoDwarfBlockEnd(); } if (!gIR->scopereturned()) { llvm::BranchInst::Create(endbb,gIR->scopebb()); } if (elsebody) { //assert(0); gIR->scope() = IRScope(elsebb,endbb); DtoDwarfBlockStart(elsebody->loc); elsebody->toIR(p); if (!gIR->scopereturned()) { llvm::BranchInst::Create(endbb,gIR->scopebb()); } DtoDwarfBlockEnd(); } // end the dwarf lexical block DtoDwarfBlockEnd(); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); } ////////////////////////////////////////////////////////////////////////////// void ScopeStatement::toIR(IRState* p) { Logger::println("ScopeStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; /*llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* beginbb = 0; // remove useless branches by clearing and reusing the current basicblock llvm::BasicBlock* bb = p->scopebb(); if (bb->empty()) { beginbb = bb; } else { beginbb = llvm::BasicBlock::Create(gIR->context(), "scope", p->topfunc(), oldend); if (!p->scopereturned()) llvm::BranchInst::Create(beginbb, bb); } llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endscope", p->topfunc(), oldend); if (beginbb != bb) p->scope() = IRScope(beginbb, endbb); else p->scope().end = endbb;*/ if (statement) { DtoDwarfBlockStart(statement->loc); statement->toIR(p); DtoDwarfBlockEnd(); } /*p->scope().end = oldend; Logger::println("Erasing scope endbb"); endbb->eraseFromParent();*/ } ////////////////////////////////////////////////////////////////////////////// void WhileStatement::toIR(IRState* p) { Logger::println("WhileStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start a dwarf lexical block DtoDwarfBlockStart(loc); // create while blocks llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* whilebb = llvm::BasicBlock::Create(gIR->context(), "whilecond", gIR->topfunc(), oldend); llvm::BasicBlock* whilebodybb = llvm::BasicBlock::Create(gIR->context(), "whilebody", gIR->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endwhile", gIR->topfunc(), oldend); // move into the while block p->ir->CreateBr(whilebb); //llvm::BranchInst::Create(whilebb, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(whilebb,endbb); // create the condition DValue* cond_e = condition->toElemDtor(p); LLValue* cond_val = DtoCast(loc, cond_e, Type::tbool)->getRVal(); delete cond_e; // conditional branch llvm::BranchInst::Create(whilebodybb, endbb, cond_val, p->scopebb()); // rewrite scope gIR->scope() = IRScope(whilebodybb,endbb); // while body code p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,whilebb,endbb)); if (body) body->toIR(p); p->func()->gen->targetScopes.pop_back(); // loop if (!gIR->scopereturned()) llvm::BranchInst::Create(whilebb, gIR->scopebb()); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); // end the dwarf lexical block DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void DoStatement::toIR(IRState* p) { Logger::println("DoStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start a dwarf lexical block DtoDwarfBlockStart(loc); // create while blocks llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* dowhilebb = llvm::BasicBlock::Create(gIR->context(), "dowhile", gIR->topfunc(), oldend); llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "dowhilecond", gIR->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "enddowhile", gIR->topfunc(), oldend); // move into the while block assert(!gIR->scopereturned()); llvm::BranchInst::Create(dowhilebb, gIR->scopebb()); // replace current scope gIR->scope() = IRScope(dowhilebb,condbb); // do-while body code p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,condbb,endbb)); if (body) body->toIR(p); p->func()->gen->targetScopes.pop_back(); // branch to condition block llvm::BranchInst::Create(condbb, gIR->scopebb()); gIR->scope() = IRScope(condbb,endbb); // create the condition DValue* cond_e = condition->toElemDtor(p); LLValue* cond_val = DtoCast(loc, cond_e, Type::tbool)->getRVal(); delete cond_e; // conditional branch llvm::BranchInst::Create(dowhilebb, endbb, cond_val, gIR->scopebb()); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); // end the dwarf lexical block DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void ForStatement::toIR(IRState* p) { Logger::println("ForStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start new dwarf lexical block DtoDwarfBlockStart(loc); // create for blocks llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* forbb = llvm::BasicBlock::Create(gIR->context(), "forcond", gIR->topfunc(), oldend); llvm::BasicBlock* forbodybb = llvm::BasicBlock::Create(gIR->context(), "forbody", gIR->topfunc(), oldend); llvm::BasicBlock* forincbb = llvm::BasicBlock::Create(gIR->context(), "forinc", gIR->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endfor", gIR->topfunc(), oldend); // init if (init != 0) init->toIR(p); // move into the for condition block, ie. start the loop assert(!gIR->scopereturned()); llvm::BranchInst::Create(forbb, gIR->scopebb()); p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,forincbb,endbb)); // replace current scope gIR->scope() = IRScope(forbb,forbodybb); // create the condition LLValue* cond_val; if (condition) { DValue* cond_e = condition->toElemDtor(p); cond_val = DtoCast(loc, cond_e, Type::tbool)->getRVal(); delete cond_e; } else { cond_val = DtoConstBool(true); } // conditional branch assert(!gIR->scopereturned()); llvm::BranchInst::Create(forbodybb, endbb, cond_val, gIR->scopebb()); // rewrite scope gIR->scope() = IRScope(forbodybb,forincbb); // do for body code if (body) body->toIR(p); // move into the for increment block if (!gIR->scopereturned()) llvm::BranchInst::Create(forincbb, gIR->scopebb()); gIR->scope() = IRScope(forincbb, endbb); // increment if (increment) { DValue* inc = increment->toElemDtor(p); delete inc; } // loop if (!gIR->scopereturned()) llvm::BranchInst::Create(forbb, gIR->scopebb()); p->func()->gen->targetScopes.pop_back(); // rewrite the scope gIR->scope() = IRScope(endbb,oldend); // end the dwarf lexical block DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void BreakStatement::toIR(IRState* p) { Logger::println("BreakStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // don't emit two terminators in a row // happens just before DMD generated default statements if the last case terminates if (p->scopereturned()) return; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); if (ident != 0) { Logger::println("ident = %s", ident->toChars()); DtoEnclosingHandlers(loc, target); // get the loop statement the label refers to Statement* targetLoopStatement = target->statement; ScopeStatement* tmp; while((tmp = targetLoopStatement->isScopeStatement())) targetLoopStatement = tmp->statement; // find the right break block and jump there bool found = false; FuncGen::TargetScopeVec::reverse_iterator it = p->func()->gen->targetScopes.rbegin(); FuncGen::TargetScopeVec::reverse_iterator it_end = p->func()->gen->targetScopes.rend(); while(it != it_end) { if(it->s == targetLoopStatement) { llvm::BranchInst::Create(it->breakTarget, p->scopebb()); found = true; break; } ++it; } assert(found); } else { // find closest scope with a break target FuncGen::TargetScopeVec::reverse_iterator it = p->func()->gen->targetScopes.rbegin(); FuncGen::TargetScopeVec::reverse_iterator it_end = p->func()->gen->targetScopes.rend(); while(it != it_end) { if(it->breakTarget) { break; } ++it; } DtoEnclosingHandlers(loc, it->s); llvm::BranchInst::Create(it->breakTarget, gIR->scopebb()); } // the break terminated this basicblock, start a new one llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterbreak", p->topfunc(), oldend); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void ContinueStatement::toIR(IRState* p) { Logger::println("ContinueStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); if (ident != 0) { Logger::println("ident = %s", ident->toChars()); DtoEnclosingHandlers(loc, target); // get the loop statement the label refers to Statement* targetLoopStatement = target->statement; ScopeStatement* tmp; while((tmp = targetLoopStatement->isScopeStatement())) targetLoopStatement = tmp->statement; // find the right continue block and jump there bool found = false; FuncGen::TargetScopeVec::reverse_iterator it = p->func()->gen->targetScopes.rbegin(); FuncGen::TargetScopeVec::reverse_iterator it_end = p->func()->gen->targetScopes.rend(); while(it != it_end) { if(it->s == targetLoopStatement) { llvm::BranchInst::Create(it->continueTarget, gIR->scopebb()); found = true; break; } ++it; } assert(found); } else { // find closest scope with a continue target FuncGen::TargetScopeVec::reverse_iterator it = p->func()->gen->targetScopes.rbegin(); FuncGen::TargetScopeVec::reverse_iterator it_end = p->func()->gen->targetScopes.rend(); while(it != it_end) { if(it->continueTarget) { break; } ++it; } DtoEnclosingHandlers(loc, it->s); llvm::BranchInst::Create(it->continueTarget, gIR->scopebb()); } // the continue terminated this basicblock, start a new one llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftercontinue", p->topfunc(), oldend); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void OnScopeStatement::toIR(IRState* p) { Logger::println("OnScopeStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; assert(statement); //statement->toIR(p); // this seems to be redundant } ////////////////////////////////////////////////////////////////////////////// void TryFinallyStatement::toIR(IRState* p) { Logger::println("TryFinallyStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); // if there's no finalbody or no body, things are simple if (!finalbody) { if (body) { DtoDwarfBlockStart(body->loc); body->toIR(p); DtoDwarfBlockEnd(); } return; } if (!body) { DtoDwarfBlockStart(finalbody->loc); finalbody->toIR(p); DtoDwarfBlockEnd(); return; } // create basic blocks llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* trybb = llvm::BasicBlock::Create(gIR->context(), "try", p->topfunc(), oldend); llvm::BasicBlock* finallybb = llvm::BasicBlock::Create(gIR->context(), "finally", p->topfunc(), oldend); // the landing pad for statements in the try block llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endtryfinally", p->topfunc(), oldend); // pass the previous BB into this assert(!gIR->scopereturned()); llvm::BranchInst::Create(trybb, p->scopebb()); // // set up the landing pad // p->scope() = IRScope(landingpadbb, endbb); assert(finalbody); IRLandingPad& pad = gIR->func()->gen->landingPadInfo; pad.addFinally(finalbody); pad.push(landingpadbb); gIR->func()->gen->targetScopes.push_back(IRTargetScope(this,new EnclosingTryFinally(this,gIR->func()->gen->landingPad),NULL,NULL)); // // do the try block // p->scope() = IRScope(trybb,finallybb); assert(body); DtoDwarfBlockStart(body->loc); body->toIR(p); DtoDwarfBlockEnd(); // terminate try BB if (!p->scopereturned()) llvm::BranchInst::Create(finallybb, p->scopebb()); pad.pop(); gIR->func()->gen->targetScopes.pop_back(); // // do finally block // p->scope() = IRScope(finallybb,landingpadbb); DtoDwarfBlockStart(finalbody->loc); finalbody->toIR(p); DtoDwarfBlockEnd(); // terminate finally //TODO: isn't it an error to have a 'returned' finally block? if (!gIR->scopereturned()) { llvm::BranchInst::Create(endbb, p->scopebb()); } // rewrite the scope p->scope() = IRScope(endbb,oldend); } ////////////////////////////////////////////////////////////////////////////// void TryCatchStatement::toIR(IRState* p) { Logger::println("TryCatchStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); // create basic blocks llvm::BasicBlock* oldend = p->scopeend(); llvm::BasicBlock* trybb = llvm::BasicBlock::Create(gIR->context(), "try", p->topfunc(), oldend); // the landing pad will be responsible for branching to the correct catch block llvm::BasicBlock* landingpadbb = llvm::BasicBlock::Create(gIR->context(), "landingpad", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endtrycatch", p->topfunc(), oldend); // pass the previous BB into this assert(!gIR->scopereturned()); llvm::BranchInst::Create(trybb, p->scopebb()); // // set up the landing pad // assert(catches); gIR->scope() = IRScope(landingpadbb, endbb); IRLandingPad& pad = gIR->func()->gen->landingPadInfo; for (unsigned i = 0; i < catches->dim; i++) { Catch *c = static_cast(catches->data[i]); pad.addCatch(c, endbb); } pad.push(landingpadbb); // // do the try block // p->scope() = IRScope(trybb,landingpadbb); assert(body); DtoDwarfBlockStart(body->loc); body->toIR(p); DtoDwarfBlockEnd(); if (!gIR->scopereturned()) llvm::BranchInst::Create(endbb, p->scopebb()); pad.pop(); // rewrite the scope p->scope() = IRScope(endbb,oldend); } ////////////////////////////////////////////////////////////////////////////// void ThrowStatement::toIR(IRState* p) { Logger::println("ThrowStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); assert(exp); DValue* e = exp->toElemDtor(p); DtoDwarfFuncEnd(gIR->func()->decl); llvm::Function* fn = LLVM_D_GetRuntimeFunction(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(); // need a block after the throw for now llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "afterthrow", p->topfunc(), oldend); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// // used to build the sorted list of cases struct Case : Object { StringExp* str; size_t index; Case(StringExp* s, size_t i) { str = s; index = i; } int compare(Object *obj) { Case* c2 = static_cast(obj); return str->compare(c2->str); } }; static LLValue* call_string_switch_runtime(llvm::Value* table, Expression* e) { Type* dt = e->type->toBasetype(); Type* dtnext = dt->nextOf()->toBasetype(); TY ty = dtnext->ty; const char* fname; if (ty == Tchar) { fname = "_d_switch_string"; } else if (ty == Twchar) { fname = "_d_switch_ustring"; } else if (ty == Tdchar) { fname = "_d_switch_dstring"; } else { llvm_unreachable("not char/wchar/dchar"); } llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, fname); if (Logger::enabled()) { Logger::cout() << *table->getType() << '\n'; Logger::cout() << *fn->getFunctionType()->getParamType(0) << '\n'; } assert(table->getType() == fn->getFunctionType()->getParamType(0)); DValue* val = e->toElemDtor(gIR); LLValue* llval = val->getRVal(); assert(llval->getType() == fn->getFunctionType()->getParamType(1)); LLCallSite call = gIR->CreateCallOrInvoke2(fn, table, llval, "tmp"); return call.getInstruction(); } void SwitchStatement::toIR(IRState* p) { Logger::println("SwitchStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); llvm::BasicBlock* oldbb = gIR->scopebb(); llvm::BasicBlock* oldend = gIR->scopeend(); // clear data from previous passes... :/ for (unsigned i=0; idim; ++i) { CaseStatement* cs = static_cast(cases->data[i]); cs->bodyBB = NULL; cs->llvmIdx = NULL; } // If one of the case expressions is non-constant, we can't use // 'switch' instruction (that can happen because D2 allows to // initialize a global variable in a static constructor). bool useSwitchInst = true; for (unsigned i=0; idim; ++i) { CaseStatement* cs = static_cast(cases->data[i]); VarDeclaration* vd = 0; if (cs->exp->op == TOKvar) vd = static_cast(cs->exp)->var->isVarDeclaration(); if (vd && (!vd->init || !vd->isConst())) { cs->llvmIdx = cs->exp->toElemDtor(p)->getRVal(); useSwitchInst = false; } } // body block. // FIXME: that block is never used llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "switchbody", p->topfunc(), oldend); // default llvm::BasicBlock* defbb = 0; if (sdefault) { Logger::println("has default"); defbb = llvm::BasicBlock::Create(gIR->context(), "default", p->topfunc(), oldend); sdefault->bodyBB = defbb; } // end (break point) llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "switchend", p->topfunc(), oldend); // do switch body assert(body); p->scope() = IRScope(bodybb, endbb); p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,NULL,endbb)); body->toIR(p); p->func()->gen->targetScopes.pop_back(); if (!p->scopereturned()) llvm::BranchInst::Create(endbb, p->scopebb()); gIR->scope() = IRScope(oldbb,oldend); if (useSwitchInst) { // string switch? llvm::Value* switchTable = 0; Array caseArray; if (!condition->type->isintegral()) { Logger::println("is string switch"); // build array of the stringexpS caseArray.reserve(cases->dim); for (unsigned i=0; idim; ++i) { CaseStatement* cs = static_cast(cases->data[i]); assert(cs->exp->op == TOKstring); caseArray.push(new Case(static_cast(cs->exp), i)); } // first sort it caseArray.sort(); // iterate and add indices to cases std::vector inits(caseArray.dim); for (size_t i=0; i(caseArray.data[i]); CaseStatement* cs = static_cast(cases->data[c->index]); cs->llvmIdx = DtoConstUint(i); inits[i] = c->str->toConstElem(p); } // build static array for ptr or final array LLType* elemTy = DtoType(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"); LLType* elemPtrTy = getPtrToType(elemTy); LLConstant* arrPtr = llvm::ConstantExpr::getBitCast(arr, elemPtrTy); // build the static table std::vector types; types.push_back(DtoSize_t()); types.push_back(elemPtrTy); LLStructType* sTy = llvm::StructType::get(gIR->context(), types); std::vector sinits; sinits.push_back(DtoConstSize_t(inits.size())); sinits.push_back(arrPtr); switchTable = llvm::ConstantStruct::get(sTy, sinits); } // condition var LLValue* condVal; // integral switch if (condition->type->isintegral()) { DValue* cond = condition->toElemDtor(p); condVal = cond->getRVal(); } // string switch else { condVal = call_string_switch_runtime(switchTable, condition); } // create switch and add the cases llvm::SwitchInst* si = llvm::SwitchInst::Create(condVal, defbb ? defbb : endbb, cases->dim, p->scopebb()); for (unsigned i=0; idim; ++i) { CaseStatement* cs = static_cast(cases->data[i]); si->addCase(isaConstantInt(cs->llvmIdx), cs->bodyBB); } } else { // we can't use switch, so we will use a bunch of br instructions instead DValue* cond = condition->toElemDtor(p); LLValue *condVal = cond->getRVal(); llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "checkcase", p->topfunc(), oldend); llvm::BranchInst::Create(nextbb, p->scopebb()); p->scope() = IRScope(nextbb, endbb); for (unsigned i=0; idim; ++i) { CaseStatement* cs = static_cast(cases->data[i]); LLValue* cmp = p->ir->CreateICmp(llvm::ICmpInst::ICMP_EQ, cs->llvmIdx, condVal, "checkcase"); nextbb = llvm::BasicBlock::Create(gIR->context(), "checkcase", p->topfunc(), oldend); llvm::BranchInst::Create(cs->bodyBB, nextbb, cmp, p->scopebb()); p->scope() = IRScope(nextbb, endbb); } if (sdefault) { llvm::BranchInst::Create(sdefault->bodyBB, p->scopebb()); } else { llvm::BranchInst::Create(endbb, p->scopebb()); } endbb->moveAfter(nextbb); } gIR->scope() = IRScope(endbb,oldend); } ////////////////////////////////////////////////////////////////////////////// void CaseStatement::toIR(IRState* p) { Logger::println("CaseStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; llvm::BasicBlock* nbb = llvm::BasicBlock::Create(gIR->context(), "case", p->topfunc(), p->scopeend()); if (bodyBB && !bodyBB->getTerminator()) { llvm::BranchInst::Create(nbb, bodyBB); } bodyBB = nbb; if (llvmIdx == NULL) { LLConstant* c = exp->toConstElem(p); llvmIdx = isaConstantInt(c); } if (!p->scopereturned()) llvm::BranchInst::Create(bodyBB, p->scopebb()); p->scope() = IRScope(bodyBB, p->scopeend()); assert(statement); DtoDwarfBlockStart(statement->loc); statement->toIR(p); DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void DefaultStatement::toIR(IRState* p) { Logger::println("DefaultStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; assert(bodyBB); llvm::BasicBlock* nbb = llvm::BasicBlock::Create(gIR->context(), "default", p->topfunc(), p->scopeend()); if (!bodyBB->getTerminator()) { llvm::BranchInst::Create(nbb, bodyBB); } bodyBB = nbb; if (!p->scopereturned()) llvm::BranchInst::Create(bodyBB, p->scopebb()); p->scope() = IRScope(bodyBB, p->scopeend()); assert(statement); DtoDwarfBlockStart(statement->loc); statement->toIR(p); DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void UnrolledLoopStatement::toIR(IRState* p) { Logger::println("UnrolledLoopStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // if no statements, there's nothing to do if (!statements || !statements->dim) return; // start a dwarf lexical block DtoDwarfBlockStart(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 llvm::BasicBlock* oldend = gIR->scopeend(); // create a block for each statement size_t nstmt = statements->dim; LLSmallVector blocks(nstmt, NULL); for (size_t i=0; icontext(), "unrolledstmt", p->topfunc(), oldend); } // create end block llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "unrolledend", p->topfunc(), oldend); // enter first stmt if (!p->scopereturned()) p->ir->CreateBr(blocks[0]); // do statements Statement** stmts = (Statement**)statements->data; for (int i=0; iscope() = IRScope(thisbb,nextbb); // push loop scope // continue goes to next statement, break goes to end p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,nextbb,endbb)); // do statement s->toIR(p); // pop loop scope p->func()->gen->targetScopes.pop_back(); // next stmt if (!p->scopereturned()) p->ir->CreateBr(nextbb); } // finish scope if (!p->scopereturned()) p->ir->CreateBr(endbb); p->scope() = IRScope(endbb,oldend); // end the dwarf lexical block DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// void ForeachStatement::toIR(IRState* p) { Logger::println("ForeachStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start a dwarf lexical block DtoDwarfBlockStart(loc); //assert(arguments->dim == 1); assert(value != 0); assert(aggr != 0); assert(func != 0); //Argument* arg = static_cast(arguments->data[0]); //Logger::println("Argument is %s", arg->toChars()); Logger::println("aggr = %s", aggr->toChars()); // key LLType* keytype = key ? DtoType(key->type) : DtoSize_t(); LLValue* keyvar; if (key) keyvar = DtoRawVarDeclaration(key); else keyvar = DtoRawAlloca(keytype, 0, "foreachkey"); // FIXME: align? LLValue* zerokey = LLConstantInt::get(keytype,0,false); // value Logger::println("value = %s", value->toPrettyChars()); LLValue* valvar = NULL; if (!value->isRef() && !value->isOut()) { // Create a local variable to serve as the value. DtoRawVarDeclaration(value); valvar = value->ir.irLocal->value; } // what to iterate DValue* aggrval = aggr->toElemDtor(p); // get length and pointer LLValue* niters = DtoArrayLen(aggrval); LLValue* val = DtoArrayPtr(aggrval); if (niters->getType() != keytype) { size_t sz1 = getTypeBitSize(niters->getType()); size_t sz2 = getTypeBitSize(keytype); if (sz1 < sz2) niters = gIR->ir->CreateZExt(niters, keytype, "foreachtrunckey"); else if (sz1 > sz2) niters = gIR->ir->CreateTrunc(niters, keytype, "foreachtrunckey"); else niters = gIR->ir->CreateBitCast(niters, keytype, "foreachtrunckey"); } if (op == TOKforeach) { new llvm::StoreInst(zerokey, keyvar, p->scopebb()); } else { new llvm::StoreInst(niters, keyvar, p->scopebb()); } llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "foreachcond", p->topfunc(), oldend); llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "foreachbody", p->topfunc(), oldend); llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "foreachnext", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "foreachend", p->topfunc(), oldend); llvm::BranchInst::Create(condbb, p->scopebb()); // condition p->scope() = IRScope(condbb,bodybb); LLValue* done = 0; LLValue* load = DtoLoad(keyvar); if (op == TOKforeach) { done = p->ir->CreateICmpULT(load, niters, "tmp"); } else if (op == TOKforeach_reverse) { done = p->ir->CreateICmpUGT(load, zerokey, "tmp"); load = p->ir->CreateSub(load, LLConstantInt::get(keytype, 1, false), "tmp"); DtoStore(load, keyvar); } llvm::BranchInst::Create(bodybb, endbb, done, p->scopebb()); // init body p->scope() = IRScope(bodybb,nextbb); // get value for this iteration LLValue* loadedKey = p->ir->CreateLoad(keyvar,"tmp"); LLValue* gep = DtoGEP1(val,loadedKey); if (!value->isRef() && !value->isOut()) { // Copy value to local variable, and use it as the value variable. DVarValue dst(value->type, valvar); DVarValue src(value->type, gep); DtoAssign(loc, &dst, &src); value->ir.irLocal->value = valvar; } else { // Use the GEP as the address of the value variable. DtoRawVarDeclaration(value, gep); } // emit body p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,nextbb,endbb)); if(body) body->toIR(p); p->func()->gen->targetScopes.pop_back(); if (!p->scopereturned()) llvm::BranchInst::Create(nextbb, p->scopebb()); // next p->scope() = IRScope(nextbb,endbb); if (op == TOKforeach) { LLValue* load = DtoLoad(keyvar); load = p->ir->CreateAdd(load, LLConstantInt::get(keytype, 1, false), "tmp"); DtoStore(load, keyvar); } llvm::BranchInst::Create(condbb, p->scopebb()); // end the dwarf lexical block DtoDwarfBlockEnd(); // end p->scope() = IRScope(endbb,oldend); } ////////////////////////////////////////////////////////////////////////////// #if DMDV2 void ForeachRangeStatement::toIR(IRState* p) { Logger::println("ForeachRangeStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // start a dwarf lexical block DtoDwarfBlockStart(loc); // evaluate lwr/upr assert(lwr->type->isintegral()); LLValue* lower = lwr->toElemDtor(p)->getRVal(); assert(upr->type->isintegral()); LLValue* upper = upr->toElemDtor(p)->getRVal(); // handle key assert(key->type->isintegral()); LLValue* keyval = DtoRawVarDeclaration(key); // store initial value in key if (op == TOKforeach) DtoStore(lower, keyval); else DtoStore(upper, keyval); // set up the block we'll need llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_cond", p->topfunc(), oldend); llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_body", p->topfunc(), oldend); llvm::BasicBlock* nextbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_next", p->topfunc(), oldend); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "foreachrange_end", p->topfunc(), oldend); // jump to condition llvm::BranchInst::Create(condbb, p->scopebb()); // CONDITION p->scope() = IRScope(condbb,bodybb); // first we test that lwr < upr lower = DtoLoad(keyval); assert(lower->getType() == upper->getType()); llvm::ICmpInst::Predicate cmpop; if (isLLVMUnsigned(key->type)) { cmpop = (op == TOKforeach) ? llvm::ICmpInst::ICMP_ULT : llvm::ICmpInst::ICMP_UGT; } else { cmpop = (op == TOKforeach) ? llvm::ICmpInst::ICMP_SLT : llvm::ICmpInst::ICMP_SGT; } LLValue* cond = p->ir->CreateICmp(cmpop, lower, upper); // jump to the body if range is ok, to the end if not llvm::BranchInst::Create(bodybb, endbb, cond, p->scopebb()); // BODY p->scope() = IRScope(bodybb,nextbb); // reverse foreach decrements here if (op == TOKforeach_reverse) { LLValue* v = DtoLoad(keyval); LLValue* one = LLConstantInt::get(v->getType(), 1, false); v = p->ir->CreateSub(v, one); DtoStore(v, keyval); } // emit body p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,nextbb,endbb)); if (body) body->toIR(p); p->func()->gen->targetScopes.pop_back(); // jump to next iteration if (!p->scopereturned()) llvm::BranchInst::Create(nextbb, p->scopebb()); // NEXT p->scope() = IRScope(nextbb,endbb); // forward foreach increments here if (op == TOKforeach) { LLValue* v = DtoLoad(keyval); LLValue* one = LLConstantInt::get(v->getType(), 1, false); v = p->ir->CreateAdd(v, one); DtoStore(v, keyval); } // jump to condition llvm::BranchInst::Create(condbb, p->scopebb()); // end the dwarf lexical block DtoDwarfBlockEnd(); // END p->scope() = IRScope(endbb,oldend); } #endif // D2 ////////////////////////////////////////////////////////////////////////////// void LabelStatement::toIR(IRState* p) { Logger::println("LabelStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // if it's an inline asm label, we don't create a basicblock, just emit it in the asm if (p->asmBlock) { IRAsmStmt* a = new IRAsmStmt; std::stringstream label; printLabelName(label, p->func()->decl->mangle(), ident->toChars()); label << ":"; a->code = label.str(); p->asmBlock->s.push_back(a); p->asmBlock->internalLabels.push_back(ident); // disable inlining gIR->func()->setNeverInline(); } else { std::string labelname = p->func()->gen->getScopedLabelName(ident->toChars()); llvm::BasicBlock*& labelBB = p->func()->gen->labelToBB[labelname]; llvm::BasicBlock* oldend = gIR->scopeend(); if (labelBB != NULL) { labelBB->moveBefore(oldend); } else { labelBB = llvm::BasicBlock::Create(gIR->context(), "label_" + labelname, p->topfunc(), oldend); } if (!p->scopereturned()) llvm::BranchInst::Create(labelBB, p->scopebb()); p->scope() = IRScope(labelBB,oldend); } if (statement) { p->func()->gen->targetScopes.push_back(IRTargetScope(this,NULL,NULL,NULL)); statement->toIR(p); p->func()->gen->targetScopes.pop_back(); } } ////////////////////////////////////////////////////////////////////////////// void GotoStatement::toIR(IRState* p) { Logger::println("GotoStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; DtoDwarfStopPoint(loc.linnum); llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergoto", p->topfunc(), oldend); DtoGoto(loc, label->ident, enclosingFinally); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void GotoDefaultStatement::toIR(IRState* p) { Logger::println("GotoDefaultStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; DtoDwarfStopPoint(loc.linnum); llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergotodefault", p->topfunc(), oldend); assert(!p->scopereturned()); assert(sw->sdefault->bodyBB); DtoEnclosingHandlers(loc, sw); llvm::BranchInst::Create(sw->sdefault->bodyBB, p->scopebb()); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void GotoCaseStatement::toIR(IRState* p) { Logger::println("GotoCaseStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; DtoDwarfStopPoint(loc.linnum); llvm::BasicBlock* oldend = gIR->scopeend(); llvm::BasicBlock* bb = llvm::BasicBlock::Create(gIR->context(), "aftergotocase", p->topfunc(), oldend); assert(!p->scopereturned()); if (!cs->bodyBB) { cs->bodyBB = llvm::BasicBlock::Create(gIR->context(), "goto_case", p->topfunc(), p->scopeend()); } DtoEnclosingHandlers(loc, sw); llvm::BranchInst::Create(cs->bodyBB, p->scopebb()); p->scope() = IRScope(bb,oldend); } ////////////////////////////////////////////////////////////////////////////// void WithStatement::toIR(IRState* p) { Logger::println("WithStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; DtoDwarfBlockStart(loc); assert(exp); // with(..) can either be used with expressions or with symbols // wthis == null indicates the symbol form if (wthis) { DValue* e = exp->toElemDtor(p); LLValue* mem = DtoRawVarDeclaration(wthis); DtoStore(e->getRVal(), mem); } if (body) body->toIR(p); DtoDwarfBlockEnd(); } ////////////////////////////////////////////////////////////////////////////// static LLConstant* generate_unique_critical_section() { LLType* Mty = DtoMutexType(); return new llvm::GlobalVariable(*gIR->module, Mty, false, llvm::GlobalValue::InternalLinkage, LLConstant::getNullValue(Mty), ".uniqueCS"); } void SynchronizedStatement::toIR(IRState* p) { Logger::println("SynchronizedStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); // enter lock if (exp) { llsync = exp->toElem(p)->getRVal(); DtoEnterMonitor(llsync); } else { llsync = generate_unique_critical_section(); DtoEnterCritical(llsync); } // emit body p->func()->gen->targetScopes.push_back(IRTargetScope(this,new EnclosingSynchro(this),NULL,NULL)); DtoDwarfBlockStart(body->loc); body->toIR(p); DtoDwarfBlockEnd(); p->func()->gen->targetScopes.pop_back(); // exit lock // no point in a unreachable unlock, terminating statements must insert this themselves. if (p->scopereturned()) return; else if (exp) DtoLeaveMonitor(llsync); else DtoLeaveCritical(llsync); } ////////////////////////////////////////////////////////////////////////////// void VolatileStatement::toIR(IRState* p) { Logger::println("VolatileStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; // emit dwarf stop point DtoDwarfStopPoint(loc.linnum); // mark in-volatile // FIXME // has statement if (statement != NULL) { // load-store DtoMemoryBarrier(false, true, false, false); // do statement p->func()->gen->targetScopes.push_back(IRTargetScope(this,new EnclosingVolatile(this),NULL,NULL)); statement->toIR(p); p->func()->gen->targetScopes.pop_back(); // no point in a unreachable barrier, terminating statements must insert this themselves. if (statement->blockExit(false) & BEfallthru) { // store-load DtoMemoryBarrier(false, false, true, false); } } // barrier only else { // load-store & store-load DtoMemoryBarrier(false, true, true, false); } // restore volatile state // FIXME } ////////////////////////////////////////////////////////////////////////////// void SwitchErrorStatement::toIR(IRState* p) { Logger::println("SwitchErrorStatement::toIR(): %s", loc.toChars()); LOG_SCOPE; llvm::Function* fn = LLVM_D_GetRuntimeFunction(gIR->module, "_d_switch_error"); std::vector args; #if DMDV2 // module param LLValue *moduleInfoSymbol = gIR->func()->decl->getModule()->moduleInfoSymbol(); LLType *moduleInfoType = DtoType(Module::moduleinfo->type); args.push_back(DtoBitCast(moduleInfoSymbol, getPtrToType(moduleInfoType))); #else // file param IrModule* irmod = getIrModule(NULL); args.push_back(DtoLoad(irmod->fileName)); #endif // line param LLConstant* c = DtoConstUint(loc.linnum); args.push_back(c); // call LLCallSite call = gIR->CreateCallOrInvoke(fn, args); call.setDoesNotReturn(); #if DMDV1 gIR->ir->CreateUnreachable(); #endif } ////////////////////////////////////////////////////////////////////////////// #if DMDV2 void ImportStatement::toIR(IRState *irs) { } #endif ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #define STUBST(x) void x::toIR(IRState * p) {error("Statement type "#x" not implemented: %s", toChars());fatal();} //STUBST(BreakStatement); //STUBST(ForStatement); //STUBST(WithStatement); //STUBST(SynchronizedStatement); //STUBST(ReturnStatement); //STUBST(ContinueStatement); //STUBST(DefaultStatement); //STUBST(CaseStatement); //STUBST(SwitchStatement); //STUBST(SwitchErrorStatement); STUBST(Statement); //STUBST(IfStatement); //STUBST(ForeachStatement); //STUBST(DoStatement); //STUBST(WhileStatement); //STUBST(ExpStatement); //STUBST(CompoundStatement); //STUBST(ScopeStatement); //STUBST(AsmStatement); //STUBST(TryCatchStatement); //STUBST(TryFinallyStatement); //STUBST(VolatileStatement); //STUBST(LabelStatement); //STUBST(ThrowStatement); //STUBST(GotoCaseStatement); //STUBST(GotoDefaultStatement); //STUBST(GotoStatement); //STUBST(UnrolledLoopStatement); //STUBST(OnScopeStatement); #if DMDV2 STUBST(PragmaStatement); #endif ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// AsmBlockStatement* Statement::endsWithAsm() { // does not end with inline asm return NULL; } AsmBlockStatement* CompoundStatement::endsWithAsm() { // make the last inner statement decide if (statements && statements->dim) { unsigned last = statements->dim - 1; Statement* s = static_cast(statements->data[last]); if (s) return s->endsWithAsm(); } return NULL; } AsmBlockStatement* AsmBlockStatement::endsWithAsm() { // yes this is inline asm return this; }