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 (ids->foundReturn) break; } else as->push(NULL); } return new UnrolledLoopStatement(loc, as); }
/***************************************** * Create inclusive postblit for struct by aggregating * all the postblits in postblits[] with the postblits for * all the members. * Note the close similarity with AggregateDeclaration::buildDtor(), * and the ordering changes (runs forward instead of backwards). */ FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc) { //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars()); StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; Loc declLoc = sd->postblits.dim ? sd->postblits[0]->loc : sd->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage for (size_t i = 0; i < sd->postblits.dim; i++) { stc |= sd->postblits[i]->storage_class & STCdisable; } Statements *a = NULL; for (size_t i = 0; i < sd->fields.dim && !(stc & STCdisable); i++) { VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->baseElemOf(); if (tv->ty != Tstruct || !v->type->size()) continue; StructDeclaration *sdv = ((TypeStruct *)tv)->sym; if (!sdv->postblit) continue; sdv->postblit->functionSemantic(); stc = mergeFuncAttrs(stc, sdv->postblit); stc = mergeFuncAttrs(stc, sdv->dtor); if (stc & STCdisable) { a = NULL; break; } if (!a) a = new Statements(); Expression *ex = new ThisExp(loc); ex = new DotVarExp(loc, ex, v, 0); if (v->type->toBasetype()->ty == Tstruct) { // this.v.__xpostblit() // This is a hack so we can call postblits on const/immutable objects. ex = new AddrExp(loc, ex); ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); ex = new PtrExp(loc, ex); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; ex = new DotVarExp(loc, ex, sdv->postblit, 0); ex = new CallExp(loc, ex); } else { // _ArrayPostblit((cast(S*)this.v.ptr)[0 .. n]) // This is a hack so we can call postblits on const/immutable objects. ex = new DotIdExp(loc, ex, Id::ptr); ex = new CastExp(loc, ex, sdv->type->pointerTo()); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; uinteger_t n = v->type->size() / sdv->type->size(); ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), new IntegerExp(loc, n, Type::tsize_t)); // Prevent redundant bounds check ((SliceExp *)ex)->upperIsInBounds = true; ((SliceExp *)ex)->lowerIsLessThanUpper = true; ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayPostblit), ex); } a->push(new ExpStatement(loc, ex)); // combine in forward order /* Bugzilla 10972: When the following field postblit calls fail, * this field should be destructed for Exception Safety. */ if (!sdv->dtor) continue; sdv->dtor->functionSemantic(); ex = new ThisExp(loc); ex = new DotVarExp(loc, ex, v, 0); if (v->type->toBasetype()->ty == Tstruct) { // this.v.__xdtor() // This is a hack so we can call destructors on const/immutable objects. ex = new AddrExp(loc, ex); ex = new CastExp(loc, ex, v->type->mutableOf()->pointerTo()); ex = new PtrExp(loc, ex); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; ex = new DotVarExp(loc, ex, sdv->dtor, 0); ex = new CallExp(loc, ex); } else { // _ArrayDtor((cast(S*)this.v.ptr)[0 .. n]) // This is a hack so we can call destructors on const/immutable objects. ex = new DotIdExp(loc, ex, Id::ptr); ex = new CastExp(loc, ex, sdv->type->pointerTo()); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; uinteger_t n = v->type->size() / sdv->type->size(); ex = new SliceExp(loc, ex, new IntegerExp(loc, 0, Type::tsize_t), new IntegerExp(loc, n, Type::tsize_t)); // Prevent redundant bounds check ((SliceExp *)ex)->upperIsInBounds = true; ((SliceExp *)ex)->lowerIsLessThanUpper = true; ex = new CallExp(loc, new IdentifierExp(loc, Id::_ArrayDtor), ex); } a->push(new OnScopeStatement(loc, TOKon_scope_failure, new ExpStatement(loc, ex))); } /* Build our own "postblit" which executes a */ if (a || (stc & STCdisable)) { //printf("Building __fieldPostBlit()\n"); PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__fieldPostblit); dd->storage_class |= STCinference; dd->fbody = a ? new CompoundStatement(loc, a) : NULL; sd->postblits.shift(dd); sd->members->push(dd); dd->semantic(sc); } FuncDeclaration *xpostblit = NULL; switch (sd->postblits.dim) { case 0: break; case 1: xpostblit = sd->postblits[0]; break; default: Expression *e = NULL; stc = STCsafe | STCnothrow | STCpure | STCnogc; for (size_t i = 0; i < sd->postblits.dim; i++) { FuncDeclaration *fd = sd->postblits[i]; stc = mergeFuncAttrs(stc, fd); if (stc & STCdisable) { e = NULL; break; } Expression *ex = new ThisExp(loc); ex = new DotVarExp(loc, ex, fd, 0); ex = new CallExp(loc, ex); e = Expression::combine(e, ex); } PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Id::__aggrPostblit); dd->storage_class |= STCinference; dd->fbody = new ExpStatement(loc, e); sd->members->push(dd); dd->semantic(sc); xpostblit = dd; break; } // Add an __xpostblit alias to make the inclusive postblit accessible if (xpostblit) { AliasDeclaration *alias = new AliasDeclaration(Loc(), Id::__xpostblit, xpostblit); alias->semantic(sc); sd->members->push(alias); alias->addMember(sc, sd); // add to symbol table } return xpostblit; }
Expression *FuncDeclaration::expandInline(InlineScanState *iss, Expression *eret, Expression *ethis, Expressions *arguments, Statement **ps) { InlineDoState ids; Expression *e = NULL; Statements *as = NULL; TypeFunction *tf = (TypeFunction*)type; #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(); VarDeclaration *vret = NULL; if (eret) { if (eret->op == TOKvar) { vret = ((VarExp *)eret)->var->isVarDeclaration(); assert(!(vret->storage_class & (STCout | STCref))); } else { /* Inlining: * this.field = foo(); // inside constructor */ vret = new VarDeclaration(loc, eret->type, Lexer::uniqueId("_satmp"), NULL); vret->storage_class |= STCtemp | STCforeach | STCref; vret->linkage = LINKd; vret->parent = iss->fd; Expression *de; de = new DeclarationExp(loc, vret); de->type = Type::tvoid; e = Expression::combine(e, de); Expression *ex; ex = new VarExp(loc, vret); ex->type = vret->type; ex = new ConstructExp(loc, ex, eret); ex->type = vret->type; e = Expression::combine(e, ex); } } // Set up vthis if (ethis) { VarDeclaration *vthis; ExpInitializer *ei; VarExp *ve; 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; 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 (ethis->type->ty != Tclass) { /* This is a reference initialization, not a simple assignment. */ ei->exp->op = TOKconstruct; } ids.vthis = vthis; } // Set up parameters if (ethis) { Expression *de = new DeclarationExp(Loc(), ids.vthis); de->type = Type::tvoid; e = Expression::combine(e, de); } if (!ps && nrvo_var) { if (vret) { ids.from.push(nrvo_var); ids.to.push(vret); } else { Identifier* tmp = Identifier::generateId("__nrvoretval"); VarDeclaration* vd = new VarDeclaration(loc, nrvo_var->type, tmp, NULL); assert(!tf->isref); vd->storage_class = STCtemp | STCrvalue; vd->linkage = tf->linkage; vd->parent = iss->fd; ids.from.push(nrvo_var); ids.to.push(vd); Expression *de = new DeclarationExp(Loc(), vd); de->type = Type::tvoid; e = Expression::combine(e, de); } } if (arguments && arguments->dim) { assert(parameters->dim == arguments->dim); for (size_t i = 0; i < arguments->dim; i++) { VarDeclaration *vfrom = (*parameters)[i]; VarDeclaration *vto; Expression *arg = (*arguments)[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 & (STCtemp | 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 ConstructExp(vto->loc, ve, arg); ei->exp->type = ve->type; //ve->type->print(); //arg->type->print(); //ei->exp->print(); ids.from.push(vfrom); ids.to.push(vto); DeclarationExp *de = new DeclarationExp(Loc(), vto); de->type = Type::tvoid; e = Expression::combine(e, de); } } if (ps) { if (e) as->push(new ExpStatement(Loc(), e)); inlineNest++; Statement *s = fbody->doInlineStatement(&ids); as->push(s); *ps = new ScopeStatement(Loc(), new CompoundStatement(Loc(), as)); inlineNest--; } else { inlineNest++; Expression *eb = fbody->doInline(&ids); e = Expression::combine(e, eb); inlineNest--; //eb->type->print(); //eb->print(); //eb->dump(0); // Bugzilla 11322: if (tf->isref) e = e->toLvalue(NULL, NULL); /* 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. * * On constructor call making __inlineretval is merely redundant, because * the returned reference is exactly same as vthis, and the 'this' variable * already exists at the caller side. */ if (tf->next->ty == Tstruct && !nrvo_var && !isCtorDeclaration()) { /* 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 = (tf->isref ? STCref : 0) | STCtemp | STCrvalue; vd->linkage = tf->linkage; vd->parent = iss->fd; VarExp *ve = new VarExp(loc, vd); ve->type = tf->next; ei->exp = new ConstructExp(loc, ve, e); ei->exp->type = ve->type; DeclarationExp* de = new DeclarationExp(Loc(), 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(); } } //printf("%s->expandInline = { %s }\n", fd->toChars(), e->toChars()); // Need to reevaluate whether parent can now be inlined // in expressions, as we might have inlined statements iss->fd->inlineStatusExp = ILSuninitialized; return e; }
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 ConstructExp(vto->loc, ve, arg); 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 = tf->isref ? STCref : 0; vd->linkage = tf->linkage; vd->parent = iss->fd; VarExp *ve = new VarExp(loc, vd); ve->type = tf->next; ei->exp = new ConstructExp(loc, ve, e); 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; }