// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. // Routines to perform function inlining #define LOG 0 #include #include #include #include // memset() #include "id.h" #include "init.h" #include "declaration.h" #include "aggregate.h" #include "expression.h" #include "statement.h" #include "mtype.h" #include "scope.h" /* ========== Compute cost of inlining =============== */ /* Walk trees to determine if inlining can be done, and if so, * if it is too complex to be worth inlining or not. */ struct InlineCostState { int nested; int hasthis; int hdrscan; // !=0 if inline scan for 'header' content FuncDeclaration *fd; }; const int COST_MAX = 250; const int STATEMENT_COST = 0x1000; const int STATEMENT_COST_MAX = 250 * 0x1000; // STATEMENT_COST be power of 2 and greater than COST_MAX //static assert((STATEMENT_COST & (STATEMENT_COST - 1)) == 0); //static assert(STATEMENT_COST > COST_MAX); bool tooCostly(int cost) { return ((cost & (STATEMENT_COST - 1)) >= COST_MAX); } int expressionInlineCost(Expression *e, InlineCostState *ics); int Statement::inlineCost(InlineCostState *ics) { //printf("Statement::inlineCost = %d\n", COST_MAX); //printf("%p\n", isScopeStatement()); //printf("%s\n", toChars()); return COST_MAX; // default is we can't inline it } int ExpStatement::inlineCost(InlineCostState *ics) { return expressionInlineCost(exp, ics); //return exp ? exp->inlineCost(ics) : 0; } int CompoundStatement::inlineCost(InlineCostState *ics) { int cost = 0; for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { cost += s->inlineCost(ics); if (tooCostly(cost)) break; } } //printf("CompoundStatement::inlineCost = %d\n", cost); return cost; } int UnrolledLoopStatement::inlineCost(InlineCostState *ics) { int cost = 0; for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { cost += s->inlineCost(ics); if (tooCostly(cost)) break; } } return cost; } int ScopeStatement::inlineCost(InlineCostState *ics) { return statement ? 1 + statement->inlineCost(ics) : 1; } int IfStatement::inlineCost(InlineCostState *ics) { int cost; #if !IN_LLVM /* Can't declare variables inside ?: expressions, so * we cannot inline if a variable is declared. */ if (arg) return COST_MAX; #endif cost = expressionInlineCost(condition, ics); #if !IN_LLVM /* Specifically allow: * if (condition) * return exp1; * else * return exp2; * Otherwise, we can't handle return statements nested in if's. */ if (elsebody && ifbody && ifbody->isReturnStatement() && elsebody->isReturnStatement()) { cost += ifbody->inlineCost(ics); cost += elsebody->inlineCost(ics); //printf("cost = %d\n", cost); } else #endif { ics->nested += 1; if (ifbody) cost += ifbody->inlineCost(ics); if (elsebody) cost += elsebody->inlineCost(ics); ics->nested -= 1; } //printf("IfStatement::inlineCost = %d\n", cost); return cost; } int ReturnStatement::inlineCost(InlineCostState *ics) { #if !IN_LLVM // Can't handle return statements nested in if's if (ics->nested) return COST_MAX; #endif return expressionInlineCost(exp, ics); } #if DMDV2 int ImportStatement::inlineCost(InlineCostState *ics) { return 0; } #endif int ForStatement::inlineCost(InlineCostState *ics) { //return COST_MAX; int cost = STATEMENT_COST; if (init) cost += init->inlineCost(ics); if (condition) cost += expressionInlineCost(condition, ics); if (increment) cost += expressionInlineCost(increment, ics); if (body) cost += body->inlineCost(ics); //printf("ForStatement: inlineCost = %d\n", cost); return cost; } /* -------------------------- */ struct ICS2 { int cost; InlineCostState *ics; }; int lambdaInlineCost(Expression *e, void *param) { ICS2 *ics2 = (ICS2 *)param; ics2->cost += e->inlineCost3(ics2->ics); return (ics2->cost >= COST_MAX); } int expressionInlineCost(Expression *e, InlineCostState *ics) { //printf("expressionInlineCost()\n"); //e->dump(0); ICS2 ics2; ics2.cost = 0; ics2.ics = ics; if (e) e->apply(&lambdaInlineCost, &ics2); return ics2.cost; } int Expression::inlineCost3(InlineCostState *ics) { return 1; } int ThisExp::inlineCost3(InlineCostState *ics) { #if !IN_LLVM //printf("ThisExp::inlineCost3() %s\n", toChars()); FuncDeclaration *fd = ics->fd; if (!fd) return COST_MAX; if (!ics->hdrscan) if (fd->isNested() || !ics->hasthis) return COST_MAX; #endif return 1; } int StructLiteralExp::inlineCost3(InlineCostState *ics) { //printf("StructLiteralExp::inlineCost3() %s\n", toChars()); #if DMDV2 if (sd->isnested) return COST_MAX; #endif return 1; } int FuncExp::inlineCost3(InlineCostState *ics) { //printf("FuncExp::inlineCost3()\n"); // This breaks on LDC too, since nested functions have internal linkage // and thus can't be referenced from other objects. // Right now, this makes the function be output to the .obj file twice. return COST_MAX; } int DelegateExp::inlineCost3(InlineCostState *ics) { //printf("DelegateExp::inlineCost3()\n"); // This breaks on LDC too, since nested functions have internal linkage // and thus can't be referenced from other objects. return COST_MAX; } int DeclarationExp::inlineCost3(InlineCostState *ics) { int cost = 0; VarDeclaration *vd; //printf("DeclarationExp::inlineCost3()\n"); vd = declaration->isVarDeclaration(); if (vd) { TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { #if 1 return COST_MAX; // finish DeclarationExp::doInline #else for (size_t i = 0; i < td->objects->dim; i++) { Object *o = (*td->objects)[i]; if (o->dyncast() != DYNCAST_EXPRESSION) return COST_MAX; Expression *eo = (Expression *)o; if (eo->op != TOKdsymbol) return COST_MAX; } return td->objects->dim; #endif } // This breaks on LDC too, since nested static variables have internal // linkage and thus can't be referenced from other objects. if (!ics->hdrscan && vd->isDataseg()) return COST_MAX; cost += 1; #if DMDV2 if (vd->edtor) // if destructor required return COST_MAX; // needs work to make this work #endif // Scan initializer (vd->init) if (vd->init) { ExpInitializer *ie = vd->init->isExpInitializer(); if (ie) { cost += expressionInlineCost(ie->exp, ics); } } } // These can contain functions, which when copied, get output twice. // These break on LDC too, since nested static variables and functions have // internal linkage and thus can't be referenced from other objects. if (declaration->isStructDeclaration() || declaration->isClassDeclaration() || declaration->isFuncDeclaration() || declaration->isTypedefDeclaration() || #if DMDV2 declaration->isAttribDeclaration() || #endif declaration->isTemplateMixin()) return COST_MAX; //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); return cost; } int CallExp::inlineCost3(InlineCostState *ics) { //printf("CallExp::inlineCost3() %s\n", toChars()); // Bugzilla 3500: super.func() calls must be devirtualized, and the inliner // can't handle that at present. if (e1->op == TOKdotvar && ((DotVarExp *)e1)->e1->op == TOKsuper) return COST_MAX; return 1; } /* ======================== Perform the inlining ============================== */ /* Inlining is done by: * o Converting to an Expression * o Copying the trees of the function to be inlined * o Renaming the variables */ struct InlineDoState { VarDeclaration *vthis; Dsymbols from; // old Dsymbols Dsymbols to; // parallel array of new Dsymbols Dsymbol *parent; // new parent FuncDeclaration *fd; // function being inlined (old parent) }; /* -------------------------------------------------------------------- */ Statement *Statement::doInlineStatement(InlineDoState *ids) { assert(0); return NULL; // default is we can't inline it } Statement *ExpStatement::doInlineStatement(InlineDoState *ids) { #if LOG if (exp) printf("ExpStatement::doInlineStatement() '%s'\n", exp->toChars()); #endif return new ExpStatement(loc, exp ? exp->doInline(ids) : NULL); } Statement *CompoundStatement::doInlineStatement(InlineDoState *ids) { //printf("CompoundStatement::doInlineStatement() %d\n", statements->dim); Statements *as = new Statements(); as->reserve(statements->dim); for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { as->push(s->doInlineStatement(ids)); if (s->isReturnStatement()) break; /* Check for: * if (condition) * return exp1; * else * return exp2; */ IfStatement *ifs = s->isIfStatement(); if (ifs && ifs->elsebody && ifs->ifbody && ifs->ifbody->isReturnStatement() && ifs->elsebody->isReturnStatement() ) break; } else as->push(NULL); } return new CompoundStatement(loc, as); } Statement *UnrolledLoopStatement::doInlineStatement(InlineDoState *ids) { //printf("UnrolledLoopStatement::doInlineStatement() %d\n", statements->dim); Statements *as = new Statements(); as->reserve(statements->dim); for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { as->push(s->doInlineStatement(ids)); if (s->isReturnStatement()) break; } else as->push(NULL); } return new UnrolledLoopStatement(loc, as); } Statement *ScopeStatement::doInlineStatement(InlineDoState *ids) { //printf("ScopeStatement::doInlineStatement() %d\n", statements->dim); return statement ? new ScopeStatement(loc, statement->doInlineStatement(ids)) : this; } Statement *IfStatement::doInlineStatement(InlineDoState *ids) { assert(!arg); Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; Statement *ifbody = this->ifbody ? this->ifbody->doInlineStatement(ids) : NULL; Statement *elsebody = this->elsebody ? this->elsebody->doInlineStatement(ids) : NULL; return new IfStatement(loc, arg, condition, ifbody, elsebody); } Statement *ReturnStatement::doInlineStatement(InlineDoState *ids) { //printf("ReturnStatement::doInlineStatement() '%s'\n", exp ? exp->toChars() : ""); return new ReturnStatement(loc, exp ? exp->doInline(ids) : NULL); } #if DMDV2 Statement *ImportStatement::doInlineStatement(InlineDoState *ids) { return NULL; } #endif Statement *ForStatement::doInlineStatement(InlineDoState *ids) { //printf("ForStatement::doInlineStatement()\n"); Statement *init = this->init ? this->init->doInlineStatement(ids) : NULL; Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; Expression *increment = this->increment ? this->increment->doInline(ids) : NULL; Statement *body = this->body ? this->body->doInlineStatement(ids) : NULL; return new ForStatement(loc, init, condition, increment, body); } /* -------------------------------------------------------------------- */ Expression *Statement::doInline(InlineDoState *ids) { printf("Statement::doInline()\n%s\n", toChars()); fflush(stdout); assert(0); return NULL; // default is we can't inline it } Expression *ExpStatement::doInline(InlineDoState *ids) { #if LOG if (exp) printf("ExpStatement::doInline() '%s'\n", exp->toChars()); #endif return exp ? exp->doInline(ids) : NULL; } Expression *CompoundStatement::doInline(InlineDoState *ids) { Expression *e = NULL; //printf("CompoundStatement::doInline() %d\n", statements->dim); for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { Expression *e2 = s->doInline(ids); e = Expression::combine(e, e2); if (s->isReturnStatement()) break; /* Check for: * if (condition) * return exp1; * else * return exp2; */ IfStatement *ifs = s->isIfStatement(); if (ifs && ifs->elsebody && ifs->ifbody && ifs->ifbody->isReturnStatement() && ifs->elsebody->isReturnStatement() ) break; } } return e; } Expression *UnrolledLoopStatement::doInline(InlineDoState *ids) { Expression *e = NULL; //printf("UnrolledLoopStatement::doInline() %d\n", statements->dim); for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) { Expression *e2 = s->doInline(ids); e = Expression::combine(e, e2); if (s->isReturnStatement()) break; } } return e; } Expression *ScopeStatement::doInline(InlineDoState *ids) { return statement ? statement->doInline(ids) : NULL; } Expression *IfStatement::doInline(InlineDoState *ids) { Expression *econd; Expression *e1; Expression *e2; Expression *e; assert(!arg); econd = condition->doInline(ids); assert(econd); if (ifbody) e1 = ifbody->doInline(ids); else e1 = NULL; if (elsebody) e2 = elsebody->doInline(ids); else e2 = NULL; if (e1 && e2) { e = new CondExp(econd->loc, econd, e1, e2); e->type = e1->type; } else if (e1) { e = new AndAndExp(econd->loc, econd, e1); e->type = Type::tvoid; } else if (e2) { e = new OrOrExp(econd->loc, econd, e2); e->type = Type::tvoid; } else { e = econd; } return e; } Expression *ReturnStatement::doInline(InlineDoState *ids) { //printf("ReturnStatement::doInline() '%s'\n", exp ? exp->toChars() : ""); return exp ? exp->doInline(ids) : 0; } #if DMDV2 Expression *ImportStatement::doInline(InlineDoState *ids) { return NULL; } #endif /* --------------------------------------------------------------- */ /****************************** * Perform doInline() on an array of Expressions. */ Expressions *arrayExpressiondoInline(Expressions *a, InlineDoState *ids) { Expressions *newa = NULL; if (a) { newa = new Expressions(); newa->setDim(a->dim); for (size_t i = 0; i < a->dim; i++) { Expression *e = a->tdata()[i]; if (e) e = e->doInline(ids); newa->tdata()[i] = e; } } return newa; } Expression *Expression::doInline(InlineDoState *ids) { //printf("Expression::doInline(%s): %s\n", Token::toChars(op), toChars()); return copy(); } Expression *SymOffExp::doInline(InlineDoState *ids) { //printf("SymOffExp::doInline(%s)\n", toChars()); for (size_t i = 0; i < ids->from.dim; i++) { if (var == ids->from.tdata()[i]) { SymOffExp *se = (SymOffExp *)copy(); se->var = (Declaration *)ids->to.tdata()[i]; return se; } } return this; } Expression *VarExp::doInline(InlineDoState *ids) { //printf("VarExp::doInline(%s)\n", toChars()); for (size_t i = 0; i < ids->from.dim; i++) { if (var == ids->from.tdata()[i]) { VarExp *ve = (VarExp *)copy(); ve->var = (Declaration *)ids->to.tdata()[i]; return ve; } } return this; } Expression *ThisExp::doInline(InlineDoState *ids) { //if (!ids->vthis) //error("no 'this' when inlining %s", ids->parent->toChars()); if (!ids->vthis) { return this; } VarExp *ve = new VarExp(loc, ids->vthis); ve->type = type; return ve; } Expression *SuperExp::doInline(InlineDoState *ids) { assert(ids->vthis); VarExp *ve = new VarExp(loc, ids->vthis); ve->type = type; return ve; } Expression *DeclarationExp::doInline(InlineDoState *ids) { DeclarationExp *de = (DeclarationExp *)copy(); VarDeclaration *vd; //printf("DeclarationExp::doInline(%s)\n", toChars()); vd = declaration->isVarDeclaration(); if (vd) { #if 0 // Need to figure this out before inlining can work for tuples TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { for (size_t i = 0; i < td->objects->dim; i++) { DsymbolExp *se = td->objects->tdata()[i]; assert(se->op == TOKdsymbol); se->s; } return st->objects->dim; } #endif if (vd->isStatic() || vd->isConst()) ; else { VarDeclaration *vto; vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); *vto = *vd; vto->parent = ids->parent; #if IN_DMD vto->csym = NULL; vto->isym = NULL; #endif ids->from.push(vd); ids->to.push(vto); if (vd->init) { if (vd->init->isVoidInitializer()) { vto->init = new VoidInitializer(vd->init->loc); } else { ExpInitializer *ie = vd->init->isExpInitializer(); assert(ie); vto->init = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); } } de->declaration = (Dsymbol *) (void *)vto; } } /* This needs work, like DeclarationExp::toElem(), if we are * to handle TemplateMixin's. For now, we just don't inline them. */ return de; } Expression *NewExp::doInline(InlineDoState *ids) { //printf("NewExp::doInline(): %s\n", toChars()); NewExp *ne = (NewExp *)copy(); if (thisexp) ne->thisexp = thisexp->doInline(ids); ne->newargs = arrayExpressiondoInline(ne->newargs, ids); ne->arguments = arrayExpressiondoInline(ne->arguments, ids); return ne; } Expression *UnaExp::doInline(InlineDoState *ids) { UnaExp *ue = (UnaExp *)copy(); ue->e1 = e1->doInline(ids); return ue; } Expression *AssertExp::doInline(InlineDoState *ids) { AssertExp *ae = (AssertExp *)copy(); ae->e1 = e1->doInline(ids); if (msg) ae->msg = msg->doInline(ids); return ae; } Expression *BinExp::doInline(InlineDoState *ids) { BinExp *be = (BinExp *)copy(); be->e1 = e1->doInline(ids); be->e2 = e2->doInline(ids); return be; } Expression *CallExp::doInline(InlineDoState *ids) { CallExp *ce; ce = (CallExp *)copy(); ce->e1 = e1->doInline(ids); ce->arguments = arrayExpressiondoInline(arguments, ids); return ce; } Expression *IndexExp::doInline(InlineDoState *ids) { IndexExp *are = (IndexExp *)copy(); are->e1 = e1->doInline(ids); if (lengthVar) { //printf("lengthVar\n"); VarDeclaration *vd = lengthVar; ExpInitializer *ie; ExpInitializer *ieto; VarDeclaration *vto; vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); *vto = *vd; vto->parent = ids->parent; #if IN_DMD vto->csym = NULL; vto->isym = NULL; #endif ids->from.push(vd); ids->to.push(vto); if (vd->init && !vd->init->isVoidInitializer()) { ie = vd->init->isExpInitializer(); assert(ie); ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); vto->init = ieto; } are->lengthVar = (VarDeclaration *) (void *)vto; } are->e2 = e2->doInline(ids); return are; } Expression *SliceExp::doInline(InlineDoState *ids) { SliceExp *are = (SliceExp *)copy(); are->e1 = e1->doInline(ids); if (lengthVar) { //printf("lengthVar\n"); VarDeclaration *vd = lengthVar; ExpInitializer *ie; ExpInitializer *ieto; VarDeclaration *vto; vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); *vto = *vd; vto->parent = ids->parent; #if IN_DMD vto->csym = NULL; vto->isym = NULL; #endif ids->from.push(vd); ids->to.push(vto); if (vd->init && !vd->init->isVoidInitializer()) { ie = vd->init->isExpInitializer(); assert(ie); ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); vto->init = ieto; } are->lengthVar = (VarDeclaration *) (void *)vto; } if (lwr) are->lwr = lwr->doInline(ids); if (upr) are->upr = upr->doInline(ids); return are; } Expression *TupleExp::doInline(InlineDoState *ids) { TupleExp *ce; ce = (TupleExp *)copy(); ce->exps = arrayExpressiondoInline(exps, ids); return ce; } Expression *ArrayLiteralExp::doInline(InlineDoState *ids) { ArrayLiteralExp *ce; ce = (ArrayLiteralExp *)copy(); ce->elements = arrayExpressiondoInline(elements, ids); return ce; } Expression *AssocArrayLiteralExp::doInline(InlineDoState *ids) { AssocArrayLiteralExp *ce; ce = (AssocArrayLiteralExp *)copy(); ce->keys = arrayExpressiondoInline(keys, ids); ce->values = arrayExpressiondoInline(values, ids); return ce; } Expression *StructLiteralExp::doInline(InlineDoState *ids) { StructLiteralExp *ce; ce = (StructLiteralExp *)copy(); ce->elements = arrayExpressiondoInline(elements, ids); return ce; } Expression *ArrayExp::doInline(InlineDoState *ids) { ArrayExp *ce; ce = (ArrayExp *)copy(); ce->e1 = e1->doInline(ids); ce->arguments = arrayExpressiondoInline(arguments, ids); return ce; } Expression *CondExp::doInline(InlineDoState *ids) { CondExp *ce = (CondExp *)copy(); ce->econd = econd->doInline(ids); ce->e1 = e1->doInline(ids); ce->e2 = e2->doInline(ids); return ce; } /* ========== Walk the parse trees, and inline expand functions ============= */ /* Walk the trees, looking for functions to inline. * Inline any that can be. */ struct InlineScanState { FuncDeclaration *fd; // function being scanned }; Statement *Statement::inlineScan(InlineScanState *iss) { return this; } Statement *ExpStatement::inlineScan(InlineScanState *iss) { #if LOG printf("ExpStatement::inlineScan(%s)\n", toChars()); #endif if (exp) { exp = exp->inlineScan(iss); /* See if we can inline as a statement rather than as * an Expression. */ if (exp && exp->op == TOKcall) { CallExp *ce = (CallExp *)exp; if (ce->e1->op == TOKvar) { VarExp *ve = (VarExp *)ce->e1; FuncDeclaration *fd = ve->var->isFuncDeclaration(); if (fd && fd != iss->fd && fd->canInline(0, 0, 1)) { Statement *s; fd->expandInline(iss, NULL, ce->arguments, &s); return s; } } } } return this; } Statement *CompoundStatement::inlineScan(InlineScanState *iss) { for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) (*statements)[i] = s->inlineScan(iss); } return this; } Statement *UnrolledLoopStatement::inlineScan(InlineScanState *iss) { for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; if (s) (*statements)[i] = s->inlineScan(iss); } return this; } Statement *ScopeStatement::inlineScan(InlineScanState *iss) { if (statement) statement = statement->inlineScan(iss); return this; } Statement *WhileStatement::inlineScan(InlineScanState *iss) { condition = condition->inlineScan(iss); body = body ? body->inlineScan(iss) : NULL; return this; } Statement *DoStatement::inlineScan(InlineScanState *iss) { body = body ? body->inlineScan(iss) : NULL; condition = condition->inlineScan(iss); return this; } Statement *ForStatement::inlineScan(InlineScanState *iss) { if (init) init = init->inlineScan(iss); if (condition) condition = condition->inlineScan(iss); if (increment) increment = increment->inlineScan(iss); if (body) body = body->inlineScan(iss); return this; } Statement *ForeachStatement::inlineScan(InlineScanState *iss) { aggr = aggr->inlineScan(iss); if (body) body = body->inlineScan(iss); return this; } #if DMDV2 Statement *ForeachRangeStatement::inlineScan(InlineScanState *iss) { lwr = lwr->inlineScan(iss); upr = upr->inlineScan(iss); if (body) body = body->inlineScan(iss); return this; } #endif Statement *IfStatement::inlineScan(InlineScanState *iss) { condition = condition->inlineScan(iss); if (ifbody) ifbody = ifbody->inlineScan(iss); if (elsebody) elsebody = elsebody->inlineScan(iss); return this; } Statement *SwitchStatement::inlineScan(InlineScanState *iss) { //printf("SwitchStatement::inlineScan()\n"); condition = condition->inlineScan(iss); body = body ? body->inlineScan(iss) : NULL; if (sdefault) sdefault = (DefaultStatement *)sdefault->inlineScan(iss); if (cases) { for (size_t i = 0; i < cases->dim; i++) { CaseStatement *s; s = cases->tdata()[i]; cases->tdata()[i] = (CaseStatement *)s->inlineScan(iss); } } return this; } Statement *CaseStatement::inlineScan(InlineScanState *iss) { //printf("CaseStatement::inlineScan()\n"); exp = exp->inlineScan(iss); if (statement) statement = statement->inlineScan(iss); return this; } Statement *DefaultStatement::inlineScan(InlineScanState *iss) { if (statement) statement = statement->inlineScan(iss); return this; } Statement *ReturnStatement::inlineScan(InlineScanState *iss) { //printf("ReturnStatement::inlineScan()\n"); if (exp) { exp = exp->inlineScan(iss); } return this; } Statement *SynchronizedStatement::inlineScan(InlineScanState *iss) { if (exp) exp = exp->inlineScan(iss); if (body) body = body->inlineScan(iss); return this; } Statement *WithStatement::inlineScan(InlineScanState *iss) { if (exp) exp = exp->inlineScan(iss); if (body) body = body->inlineScan(iss); return this; } Statement *TryCatchStatement::inlineScan(InlineScanState *iss) { if (body) body = body->inlineScan(iss); if (catches) { for (size_t i = 0; i < catches->dim; i++) { Catch *c = catches->tdata()[i]; if (c->handler) c->handler = c->handler->inlineScan(iss); } } return this; } Statement *TryFinallyStatement::inlineScan(InlineScanState *iss) { if (body) body = body->inlineScan(iss); if (finalbody) finalbody = finalbody->inlineScan(iss); return this; } Statement *ThrowStatement::inlineScan(InlineScanState *iss) { if (exp) exp = exp->inlineScan(iss); return this; } Statement *VolatileStatement::inlineScan(InlineScanState *iss) { if (statement) statement = statement->inlineScan(iss); return this; } Statement *LabelStatement::inlineScan(InlineScanState *iss) { if (statement) statement = statement->inlineScan(iss); return this; } /* -------------------------- */ void arrayInlineScan(InlineScanState *iss, Expressions *arguments) { if (arguments) { for (size_t i = 0; i < arguments->dim; i++) { Expression *e = arguments->tdata()[i]; if (e) { e = e->inlineScan(iss); arguments->tdata()[i] = e; } } } } Expression *Expression::inlineScan(InlineScanState *iss) { return this; } void scanVar(Dsymbol *s, InlineScanState *iss) { VarDeclaration *vd = s->isVarDeclaration(); if (vd) { TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); if (td) { for (size_t i = 0; i < td->objects->dim; i++) { DsymbolExp *se = (DsymbolExp *)td->objects->tdata()[i]; assert(se->op == TOKdsymbol); scanVar(se->s, iss); } } else { // Scan initializer (vd->init) if (vd->init) { ExpInitializer *ie = vd->init->isExpInitializer(); if (ie) { #if DMDV2 if (vd->type) { Type *tb = vd->type->toBasetype(); if (tb->ty == Tstruct) { StructDeclaration *sd = ((TypeStruct *)tb)->sym; if (sd->cpctor) { /* The problem here is that if the initializer is a * function call that returns a struct S with a cpctor: * S s = foo(); * the postblit is done by the return statement in foo() * in s2ir.c, the intermediate code generator. * But, if foo() is inlined and now the code looks like: * S s = x; * the postblit is not there, because such assignments * are rewritten as s.cpctor(&x) by the front end. * So, the inlining won't get the postblit called. * Work around by not inlining these cases. * A proper fix would be to move all the postblit * additions to the front end. */ return; } } } #endif ie->exp = ie->exp->inlineScan(iss); } } } } } Expression *DeclarationExp::inlineScan(InlineScanState *iss) { //printf("DeclarationExp::inlineScan()\n"); scanVar(declaration, iss); return this; } Expression *UnaExp::inlineScan(InlineScanState *iss) { e1 = e1->inlineScan(iss); return this; } Expression *AssertExp::inlineScan(InlineScanState *iss) { e1 = e1->inlineScan(iss); if (msg) msg = msg->inlineScan(iss); return this; } Expression *BinExp::inlineScan(InlineScanState *iss) { e1 = e1->inlineScan(iss); e2 = e2->inlineScan(iss); return this; } Expression *CallExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("CallExp::inlineScan()\n"); e1 = e1->inlineScan(iss); arrayInlineScan(iss, arguments); if (e1->op == TOKvar) { VarExp *ve = (VarExp *)e1; FuncDeclaration *fd = ve->var->isFuncDeclaration(); if (fd && fd != iss->fd && fd->canInline(0, 0, 0)) { e = fd->expandInline(iss, NULL, arguments, NULL); } } else if (e1->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)e1; FuncDeclaration *fd = dve->var->isFuncDeclaration(); if (fd && fd != iss->fd && fd->canInline(1, 0, 0)) { if (dve->e1->op == TOKcall && dve->e1->type->toBasetype()->ty == Tstruct) { /* To create ethis, we'll need to take the address * of dve->e1, but this won't work if dve->e1 is * a function call. */ ; } else e = fd->expandInline(iss, dve->e1, arguments, NULL); } } return e; } Expression *SliceExp::inlineScan(InlineScanState *iss) { e1 = e1->inlineScan(iss); if (lwr) lwr = lwr->inlineScan(iss); if (upr) upr = upr->inlineScan(iss); return this; } Expression *TupleExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("TupleExp::inlineScan()\n"); arrayInlineScan(iss, exps); return e; } Expression *ArrayLiteralExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("ArrayLiteralExp::inlineScan()\n"); arrayInlineScan(iss, elements); return e; } Expression *AssocArrayLiteralExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("AssocArrayLiteralExp::inlineScan()\n"); arrayInlineScan(iss, keys); arrayInlineScan(iss, values); return e; } Expression *StructLiteralExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("StructLiteralExp::inlineScan()\n"); arrayInlineScan(iss, elements); return e; } Expression *ArrayExp::inlineScan(InlineScanState *iss) { Expression *e = this; //printf("ArrayExp::inlineScan()\n"); e1 = e1->inlineScan(iss); arrayInlineScan(iss, arguments); return e; } Expression *CondExp::inlineScan(InlineScanState *iss) { econd = econd->inlineScan(iss); e1 = e1->inlineScan(iss); e2 = e2->inlineScan(iss); return this; } /* ========== =============== */ void FuncDeclaration::inlineScan() { InlineScanState iss; #if LOG printf("FuncDeclaration::inlineScan('%s')\n", toChars()); #endif memset(&iss, 0, sizeof(iss)); iss.fd = this; if (fbody && !naked) { inlineNest++; fbody = fbody->inlineScan(&iss); inlineNest--; } } int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) { InlineCostState ics; int cost; #define CANINLINE_LOG 0 #if CANINLINE_LOG printf("FuncDeclaration::canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, toChars()); #endif if (needThis() && !hasthis) return 0; if (inlineNest || (semanticRun < PASSsemantic3 && !hdrscan)) { #if CANINLINE_LOG printf("\t1: no, inlineNest = %d, semanticRun = %d\n", inlineNest, semanticRun); #endif return 0; } switch (statementsToo ? inlineStatusStmt : inlineStatusExp) { case ILSyes: #if CANINLINE_LOG printf("\t1: yes %s\n", toChars()); #endif return 1; case ILSno: #if CANINLINE_LOG printf("\t1: no %s\n", toChars()); #endif return 0; case ILSuninitialized: break; default: assert(0); } if (type) { assert(type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)(type); #if IN_LLVM // LDC: Only extern(C) varargs count. if (tf->linkage != LINKd) #endif if (tf->varargs == 1) // no variadic parameter lists goto Lno; /* Don't inline a function that returns non-void, but has * no return expression. * No statement inlining for non-voids. */ if (tf->next && tf->next->ty != Tvoid && (!(hasReturnExp & 1) || statementsToo) && !hdrscan) goto Lno; } #if !IN_LLVM // LDC: Only extern(C) varargs count, and ctors use extern(D). else { CtorDeclaration *ctor = isCtorDeclaration(); if (ctor && ctor->varargs == 1) goto Lno; } #endif if ( !fbody || ident == Id::ensure || // ensure() has magic properties the inliner loses (ident == Id::require && // require() has magic properties too toParent()->isFuncDeclaration() && // see bug 7699 toParent()->isFuncDeclaration()->needThis()) || !hdrscan && ( #if 0 isCtorDeclaration() || // cannot because need to convert: // return; // to: // return this; #endif isSynchronized() || isImportedSymbol() || #if !IN_LLVM hasNestedFrameRefs() || // no nested references to this frame #endif // !IN_LLVM (isVirtual() && !isFinal()) )) { goto Lno; } #if !IN_LLVM #if !SARRAYVALUE /* If any parameters are Tsarray's (which are passed by reference) * or out parameters (also passed by reference), don't do inlining. */ if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = parameters->tdata()[i]; if (v->type->toBasetype()->ty == Tsarray) goto Lno; } } #endif #endif memset(&ics, 0, sizeof(ics)); ics.hasthis = hasthis; ics.fd = this; ics.hdrscan = hdrscan; cost = fbody->inlineCost(&ics); #if CANINLINE_LOG printf("cost = %d for %s\n", cost, toChars()); #endif if (tooCostly(cost)) goto Lno; if (!statementsToo && cost > COST_MAX) goto Lno; if (!hdrscan) { // Don't modify inlineStatus for header content scan if (statementsToo) inlineStatusStmt = ILSyes; else inlineStatusExp = ILSyes; #if !IN_LLVM inlineScan(); // Don't scan recursively for header content scan #endif if (inlineStatusExp == ILSuninitialized) { // Need to redo cost computation, as some statements or expressions have been inlined memset(&ics, 0, sizeof(ics)); ics.hasthis = hasthis; ics.fd = this; ics.hdrscan = hdrscan; cost = fbody->inlineCost(&ics); #if CANINLINE_LOG printf("recomputed cost = %d for %s\n", cost, toChars()); #endif if (tooCostly(cost)) goto Lno; if (!statementsToo && cost > COST_MAX) goto Lno; if (statementsToo) inlineStatusStmt = ILSyes; else inlineStatusExp = ILSyes; } } #if CANINLINE_LOG printf("\t2: yes %s\n", toChars()); #endif return 1; Lno: if (!hdrscan) // Don't modify inlineStatus for header content scan { if (statementsToo) inlineStatusStmt = ILSno; else inlineStatusExp = ILSno; } #if CANINLINE_LOG printf("\t2: no %s\n", toChars()); #endif return 0; } Expression *FuncDeclaration::expandInline(InlineScanState *iss, Expression *ethis, Expressions *arguments, Statement **ps) { InlineDoState ids; DeclarationExp *de; Expression *e = NULL; Statements *as = NULL; #if LOG || CANINLINE_LOG printf("FuncDeclaration::expandInline('%s')\n", toChars()); #endif memset(&ids, 0, sizeof(ids)); ids.parent = iss->fd; ids.fd = this; if (ps) as = new Statements(); // Set up vthis if (ethis) { VarDeclaration *vthis; ExpInitializer *ei; VarExp *ve; #if STRUCTTHISREF if (ethis->type->ty == Tpointer) { Type *t = ethis->type->nextOf(); ethis = new PtrExp(ethis->loc, ethis); ethis->type = t; } ei = new ExpInitializer(ethis->loc, ethis); vthis = new VarDeclaration(ethis->loc, ethis->type, Id::This, ei); if (ethis->type->ty != Tclass) vthis->storage_class = STCref; else vthis->storage_class = STCin; #else if (ethis->type->ty != Tclass && ethis->type->ty != Tpointer) { ethis = ethis->addressOf(NULL); } ei = new ExpInitializer(ethis->loc, ethis); vthis = new VarDeclaration(ethis->loc, ethis->type, Id::This, ei); vthis->storage_class = STCin; #endif vthis->linkage = LINKd; vthis->parent = iss->fd; ve = new VarExp(vthis->loc, vthis); ve->type = vthis->type; ei->exp = new AssignExp(vthis->loc, ve, ethis); ei->exp->type = ve->type; #if STRUCTTHISREF if (ethis->type->ty != Tclass) { /* This is a reference initialization, not a simple assignment. */ ei->exp->op = TOKconstruct; } #endif ids.vthis = vthis; } // Set up parameters if (ethis) { e = new DeclarationExp(0, ids.vthis); e->type = Type::tvoid; if (as) as->push(new ExpStatement(e->loc, e)); } if (arguments && arguments->dim) { assert(parameters->dim == arguments->dim); for (size_t i = 0; i < arguments->dim; i++) { VarDeclaration *vfrom = parameters->tdata()[i]; VarDeclaration *vto; Expression *arg = arguments->tdata()[i]; ExpInitializer *ei; VarExp *ve; ei = new ExpInitializer(arg->loc, arg); vto = new VarDeclaration(vfrom->loc, vfrom->type, vfrom->ident, ei); vto->storage_class |= vfrom->storage_class & (STCin | STCout | STClazy | STCref); vto->linkage = vfrom->linkage; vto->parent = iss->fd; //printf("vto = '%s', vto->storage_class = x%x\n", vto->toChars(), vto->storage_class); //printf("vto->parent = '%s'\n", iss->fd->toChars()); ve = new VarExp(vto->loc, vto); //ve->type = vto->type; ve->type = arg->type; ei->exp = new AssignExp(vto->loc, ve, arg); ei->exp->op = TOKconstruct; ei->exp->type = ve->type; //ve->type->print(); //arg->type->print(); //ei->exp->print(); ids.from.push(vfrom); ids.to.push(vto); de = new DeclarationExp(0, vto); de->type = Type::tvoid; if (as) as->push(new ExpStatement(0, de)); else e = Expression::combine(e, de); } } if (ps) { inlineNest++; Statement *s = fbody->doInlineStatement(&ids); as->push(s); *ps = new ScopeStatement(0, new CompoundStatement(0, as)); inlineNest--; } else { inlineNest++; Expression *eb = fbody->doInline(&ids); e = Expression::combine(e, eb); inlineNest--; //eb->type->print(); //eb->print(); //eb->dump(0); } /* There's a problem if what the function returns is used subsequently as an * lvalue, as in a struct return that is then used as a 'this'. * If we take the address of the return value, we will be taking the address * of the original, not the copy. Fix this by assigning the return value to * a temporary, then returning the temporary. If the temporary is used as an * lvalue, it will work. * This only happens with struct returns. * See Bugzilla 2127 for an example. */ TypeFunction *tf = (TypeFunction*)type; if (!ps && tf->next->ty == Tstruct) { /* Generate a new variable to hold the result and initialize it with the * inlined body of the function: * tret __inlineretval = e; */ ExpInitializer* ei = new ExpInitializer(loc, e); Identifier* tmp = Identifier::generateId("__inlineretval"); VarDeclaration* vd = new VarDeclaration(loc, tf->next, tmp, ei); vd->storage_class = 0; vd->linkage = tf->linkage; vd->parent = iss->fd; VarExp *ve = new VarExp(loc, vd); ve->type = tf->next; ei->exp = new AssignExp(loc, ve, e); ei->exp->op = TOKconstruct; ei->exp->type = ve->type; DeclarationExp* de = new DeclarationExp(0, vd); de->type = Type::tvoid; // Chain the two together: // ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval e = Expression::combine(de, ve); //fprintf(stderr, "CallExp::inlineScan: e = "); e->print(); } // Need to reevaluate whether parent can now be inlined // in expressions, as we might have inlined statements iss->fd->inlineStatusExp = ILSuninitialized; return e; } /**************************************************** * Perform the "inline copying" of a default argument for a function parameter. */ Expression *Expression::inlineCopy(Scope *sc) { #if 0 /* See Bugzilla 2935 for explanation of why just a copy() is broken */ return copy(); #else if (op == TOKdelegate) { DelegateExp *de = (DelegateExp *)this; if (de->func->isNested()) { /* See Bugzilla 4820 * Defer checking until later if we actually need the 'this' pointer */ Expression *e = de->copy(); return e; } } InlineCostState ics; memset(&ics, 0, sizeof(ics)); ics.hdrscan = 1; // so DeclarationExp:: will work on 'statics' which are not int cost = expressionInlineCost(this, &ics); if (cost >= COST_MAX) { error("cannot inline default argument %s", toChars()); return new ErrorExp(); } InlineDoState ids; memset(&ids, 0, sizeof(ids)); ids.parent = sc->parent; Expression *e = doInline(&ids); return e; #endif }