38 changes: 26 additions & 12 deletions src/e2ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ void objc_callfunc_setupEp(elem *esel, elem **ep, int reverse);
*/
bool ISREF(Declaration *var, Type *tb)
{
return (var->isParameter() && config.exe == EX_WIN64 && (var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
return (config.exe == EX_WIN64 && var->isParameter() &&
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
|| var->isOut() || var->isRef();
}

Expand All @@ -91,8 +92,8 @@ bool ISREF(Declaration *var, Type *tb)
bool ISWIN64REF(Declaration *var)
{
return (config.exe == EX_WIN64 && var->isParameter() &&
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy)) &&
!(var->isOut() || var->isRef());
(var->type->size(Loc()) > REGSIZE || var->storage_class & STClazy))
&& !(var->isOut() || var->isRef());
}

/******************************************
Expand Down Expand Up @@ -945,7 +946,6 @@ elem *toElem(Expression *e, IRState *irs)
void visit(SymbolExp *se)
{
elem *e;
tym_t tym;
Type *tb = (se->op == TOKsymoff) ? se->var->type->toBasetype() : se->type->toBasetype();
int offset = (se->op == TOKsymoff) ? ((SymOffExp*)se)->offset : 0;
VarDeclaration *v = se->var->isVarDeclaration();
Expand Down Expand Up @@ -1089,7 +1089,6 @@ elem *toElem(Expression *e, IRState *irs)
}
else if (ISREF(se->var, tb))
{
// Static arrays are really passed as pointers to the array
// Out parameters are really references
e = el_var(s);
e->Ety = TYnptr;
Expand All @@ -1113,13 +1112,17 @@ elem *toElem(Expression *e, IRState *irs)
e->Ety = TYnptr;
e = el_una(OPind, 0, e);
}
if (tb->ty == Tfunction)
{

tym_t tym;
if (se->var->storage_class & STClazy)
tym = TYdelegate; // Tdelegate as C type
else if (tb->ty == Tfunction)
tym = s->Stype->Tty;
}
else
tym = totym(se->type);

e->Ejty = e->Ety = tym;

if (tybasic(tym) == TYstruct)
{
e->ET = Type_toCtype(se->type);
Expand Down Expand Up @@ -2505,7 +2508,7 @@ elem *toElem(Expression *e, IRState *irs)
Type *ta = are->e1->type->toBasetype();

// which we do if the 'next' types match
if (ae->ismemset & 1)
if (ae->memset & MemorySet_blockAssign)
{
// Do a memset for array[]=v
//printf("Lpair %s\n", ae->toChars());
Expand Down Expand Up @@ -2695,11 +2698,14 @@ elem *toElem(Expression *e, IRState *irs)

/* Look for reference initializations
*/
if (ae->op == TOKconstruct && ae->e1->op == TOKvar && !(ae->ismemset & 2))
if (ae->memset & MemorySet_referenceInit)
{
assert(ae->op == TOKconstruct || ae->op == TOKblit);
assert(ae->e1->op == TOKvar);

VarExp *ve = (VarExp *)ae->e1;
Declaration *s = ve->var;
if (s->storage_class & (STCout | STCref))
Declaration *d = ve->var;
if (d->storage_class & (STCout | STCref))
{
e = toElem(ae->e2, irs);
e = addressElem(e, ae->e2->type);
Expand Down Expand Up @@ -2743,6 +2749,14 @@ elem *toElem(Expression *e, IRState *irs)
//printf("e1x = \n"); elem_print(e1x);
}

// inlining may generate lazy variable initialization
if (ae->e1->op == TOKvar && (((VarExp *)ae->e1)->var->storage_class & STClazy))
{
assert(ae->op == TOKconstruct || ae->op == TOKblit);
e = el_bin(OPeq, tym, e1, toElem(ae->e2, irs));
goto Lret;
}

/* This will work if we can distinguish an assignment from
* an initialization of the lvalue. It'll work if the latter.
* If the former, because of aliasing of the return value with
Expand Down
180 changes: 150 additions & 30 deletions src/expression.d

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions src/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -1165,12 +1165,16 @@ class PreExp : public UnaExp
void accept(Visitor *v) { v->visit(this); }
};

enum MemorySet
{
MemorySet_blockAssign = 1, // setting the contents of an array
MemorySet_referenceInit = 2, // setting the reference of STCref variable
};

class AssignExp : public BinExp
{
public:
// &1 != 0 if setting the contents of an array
// &2 != 0 if setting the content of ref variable
int ismemset;
int memset; // combination of MemorySet flags

AssignExp(Loc loc, Expression *e1, Expression *e2);
Expression *semantic(Scope *sc);
Expand All @@ -1185,13 +1189,15 @@ class ConstructExp : public AssignExp
{
public:
ConstructExp(Loc loc, Expression *e1, Expression *e2);
ConstructExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};

class BlitExp : public AssignExp
{
public:
BlitExp(Loc loc, Expression *e1, Expression *e2);
BlitExp(Loc loc, VarDeclaration *v, Expression *e2);
void accept(Visitor *v) { v->visit(this); }
};

Expand Down
14 changes: 5 additions & 9 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,7 @@ public:
assert(tret.ty != Tvoid);
if (vresult || returnLabel)
buildResultVar(scout ? scout : sc2, tret);

/* Cannot move this loop into NrvoWalker, because
* returns[i] may be in the nested delegate for foreach-body.
*/
Expand All @@ -1817,6 +1818,7 @@ public:
else
{
exp = exp.optimize(WANTvalue);

/* Bugzilla 10789:
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
Expand All @@ -1828,13 +1830,8 @@ public:
if (vresult)
{
// Create: return vresult = exp;
auto ve = new VarExp(Loc(), vresult);
ve.type = vresult.type;
if (f.isref)
exp = new ConstructExp(rs.loc, ve, exp);
else
exp = new BlitExp(rs.loc, ve, exp);
exp.type = ve.type;
exp = new BlitExp(rs.loc, vresult, exp);
exp.type = vresult.type;
if (rs.caseDim)
exp = Expression.combine(exp, new IntegerExp(rs.caseDim));
}
Expand Down Expand Up @@ -2018,8 +2015,7 @@ public:
*/
Expression e = new VarExp(Loc(), v_arguments);
e = new DotIdExp(Loc(), e, Id.elements);
Expression e1 = new VarExp(Loc(), _arguments);
e = new ConstructExp(Loc(), e1, e);
e = new ConstructExp(Loc(), _arguments, e);
e = e.semantic(sc2);
_arguments._init = new ExpInitializer(Loc(), e);
auto de = new DeclarationExp(Loc(), _arguments);
Expand Down
123 changes: 80 additions & 43 deletions src/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -1735,13 +1735,16 @@ public:
bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsToo)
{
int cost;

enum CANINLINE_LOG = 0;
static if (CANINLINE_LOG)
{
printf("FuncDeclaration.canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, fd.toPrettyChars());
}

if (fd.needThis() && !hasthis)
return false;

if (fd.inlineNest)
{
static if (CANINLINE_LOG)
Expand All @@ -1750,6 +1753,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
}
return false;
}

if (fd.semanticRun < PASSsemantic3 && !hdrscan)
{
if (!fd.fbody)
Expand All @@ -1761,6 +1765,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
return false;
assert(fd.semanticRun >= PASSsemantic3done);
}

switch (statementsToo ? fd.inlineStatusStmt : fd.inlineStatusExp)
{
case ILSyes:
Expand All @@ -1780,6 +1785,7 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
default:
assert(0);
}

switch (fd.inlining)
{
case PINLINEdefault:
Expand All @@ -1791,23 +1797,33 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
default:
assert(0);
}

if (fd.type)
{
assert(fd.type.ty == Tfunction);
TypeFunction tf = cast(TypeFunction)fd.type;
if (tf.varargs == 1) // no variadic parameter lists

// no variadic parameter lists
if (tf.varargs == 1)
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 && (!(fd.hasReturnExp & 1) || statementsToo) && !hdrscan)
if (tf.next && tf.next.ty != Tvoid &&
(!(fd.hasReturnExp & 1) || statementsToo) &&
!hdrscan)
{
goto Lno;
}

/* Bugzilla 14560: If fd returns void, all explicit `return;`s
* must not appear in the expanded result.
* See also ReturnStatement.inlineAsStatement().
*/
}

// cannot inline constructor calls because we need to convert:
// return;
// to:
Expand All @@ -1816,10 +1832,19 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
// require() has magic properties too
// see bug 7699
// no nested references to this frame
if (!fd.fbody || fd.ident == Id.ensure || (fd.ident == Id.require && fd.toParent().isFuncDeclaration() && fd.toParent().isFuncDeclaration().needThis()) || !hdrscan && (fd.isSynchronized() || fd.isImportedSymbol() || fd.hasNestedFrameRefs() || (fd.isVirtual() && !fd.isFinalFunc())))
if (!fd.fbody ||
fd.ident == Id.ensure ||
(fd.ident == Id.require &&
fd.toParent().isFuncDeclaration() &&
fd.toParent().isFuncDeclaration().needThis()) ||
!hdrscan && (fd.isSynchronized() ||
fd.isImportedSymbol() ||
fd.hasNestedFrameRefs() ||
(fd.isVirtual() && !fd.isFinalFunc())))
{
goto Lno;
}

{
scope InlineCostVisitor icv = new InlineCostVisitor();
icv.hasthis = hasthis;
Expand All @@ -1832,19 +1857,23 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
{
printf("cost = %d for %s\n", cost, fd.toChars());
}

if (tooCostly(cost))
goto Lno;
if (!statementsToo && cost > COST_MAX)
goto Lno;

if (!hdrscan)
{
// Don't modify inlineStatus for header content scan
if (statementsToo)
fd.inlineStatusStmt = ILSyes;
else
fd.inlineStatusExp = ILSyes;

scope InlineScanVisitor v = new InlineScanVisitor();
fd.accept(v); // Don't scan recursively for header content scan

if (fd.inlineStatusExp == ILSuninitialized)
{
// Need to redo cost computation, as some statements or expressions have been inlined
Expand All @@ -1858,10 +1887,12 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
{
printf("recomputed cost = %d for %s\n", cost, fd.toChars());
}

if (tooCostly(cost))
goto Lno;
if (!statementsToo && cost > COST_MAX)
goto Lno;

if (statementsToo)
fd.inlineStatusStmt = ILSyes;
else
Expand All @@ -1873,9 +1904,11 @@ bool canInline(FuncDeclaration fd, bool hasthis, bool hdrscan, bool statementsTo
printf("\t2: yes %s\n", fd.toChars());
}
return true;

Lno:
if (fd.inlining == PINLINEalways)
fd.error("cannot inline function");

if (!hdrscan) // Don't modify inlineStatus for header content scan
{
if (statementsToo)
Expand All @@ -1902,10 +1935,13 @@ public void inlineScanModule(Module m)
if (m.semanticRun != PASSsemantic3done)
return;
m.semanticRun = PASSinline;

// Note that modules get their own scope, from scratch.
// This is so regardless of where in the syntax a module
// gets imported, it is unaffected by context.

//printf("Module = %p\n", m.sc.scopesym);

foreach (i; 0 .. m.members.dim)
{
Dsymbol s = (*m.members)[i];
Expand Down Expand Up @@ -1950,6 +1986,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,

if (asStatements)
as = new Statements();

VarDeclaration vret = null;
if (eret)
{
Expand All @@ -1967,55 +2004,50 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
vret.storage_class |= STCtemp | STCforeach | STCref;
vret.linkage = LINKd;
vret.parent = parent;

Expression de = new DeclarationExp(fd.loc, vret);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expression ex = new VarExp(fd.loc, vret);
ex.type = vret.type;
ex = new ConstructExp(fd.loc, ex, eret);

Expression ex = new ConstructExp(fd.loc, vret, 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);
auto ei = new ExpInitializer(ethis.loc, ethis);

auto 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 = parent;
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;
}

ei.exp = new ConstructExp(vthis.loc, vthis, ethis);
ei.exp.type = vthis.type;

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 (!asStatements && fd.nrvo_var)
{
if (vret)
Expand All @@ -2031,8 +2063,10 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
vd.storage_class = STCtemp | STCrvalue;
vd.linkage = tf.linkage;
vd.parent = parent;

ids.from.push(fd.nrvo_var);
ids.to.push(vd);

Expression de = new DeclarationExp(Loc(), vd);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expand All @@ -2043,31 +2077,27 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
assert(fd.parameters.dim == arguments.dim);
foreach (i; 0 .. arguments.dim)
{
VarDeclaration vfrom = (*fd.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);
auto vfrom = (*fd.parameters)[i];

auto arg = (*arguments)[i];
auto ei = new ExpInitializer(arg.loc, arg);

auto 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 = parent;
//printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
//printf("vto.parent = '%s'\n", parent.toChars());
ve = new VarExp(vto.loc, vto);
//ve.type = vto.type;
ve.type = arg.type;
if (vfrom.storage_class & (STCout | STCref))
ei.exp = new ConstructExp(vto.loc, ve, arg);
else
ei.exp = new BlitExp(vto.loc, ve, arg);
ei.exp.type = ve.type;
//ve.type.print();

// Even if vto is STClazy, `vto = arg` is handled correctly in glue layer.
ei.exp = new BlitExp(vto.loc, vto, arg);
ei.exp.type = vto.type;
//arg.type.print();
//ei.exp.print();

ids.from.push(vfrom);
ids.to.push(vto);

auto de = new DeclarationExp(Loc(), vto);
de.type = Type.tvoid;
e = Expression.combine(e, de);
Expand All @@ -2083,6 +2113,7 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
}
}
}

if (asStatements)
{
if (e)
Expand All @@ -2105,9 +2136,11 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
//eb.type.print();
//eb.print();
//eb.print();

// 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
Expand All @@ -2128,25 +2161,29 @@ void expandInline(FuncDeclaration fd, FuncDeclaration parent, Expression eret,
* tret __inlineretval = e;
*/
auto ei = new ExpInitializer(fd.loc, e);

Identifier tmp = Identifier.generateId("__inlineretval");
auto vd = new VarDeclaration(fd.loc, tf.next, tmp, ei);
vd.storage_class = (tf.isref ? STCref : 0) | STCtemp | STCrvalue;
vd.linkage = tf.linkage;
vd.parent = parent;
auto ve = new VarExp(fd.loc, vd);
ve.type = tf.next;
ei.exp = new ConstructExp(fd.loc, ve, e);
ei.exp.type = ve.type;

ei.exp = new ConstructExp(fd.loc, vd, e);
ei.exp.type = vd.type;

auto de = new DeclarationExp(Loc(), vd);
de.type = Type.tvoid;

// Chain the two together:
// ( typeof(return) __inlineretval = ( inlined body )) , __inlineretval
e = Expression.combine(de, ve);
e = Expression.combine(de, new VarExp(fd.loc, vd));

//fprintf(stderr, "CallExp.inlineScan: e = "); e.print();
}
eresult = e;
}
//printf("%s.expandInline = { %s }\n", fd.toChars(), e.toChars());
//printf("[%s] %s expandInline = { %s }\n", fd.loc.toChars(), fd.toPrettyChars(), e.toChars());

// Need to reevaluate whether parent can now be inlined
// in expressions, as we might have inlined statements
parent.inlineStatusExp = ILSuninitialized;
Expand Down
45 changes: 45 additions & 0 deletions test/runnable/inline.d
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,50 @@ void test9785_2() {
loop(&func);
}

/**********************************/
// 15207

struct Vec15207
{
float x, y, z;

this(float x_, float y_, float z_)
{
x = x_;
y = y_;
z = z_;
}

Vec15207 clone()
{
// When the variable 'res' is replaced with a STCref temporary,
// this line was accidentally changed to reference initialization.
Vec15207 res = this;

return res;
}
}

class C15207
{
Vec15207 a;

this()
{
a = Vec15207(1, 2, 3).clone();

assert(a.x == 1);
assert(a.y == 2);
assert(a.z == 3);
printf("%f %f %f\n", a.x, a.y, a.z);
}
}

void test15207()
{
auto c = new C15207();
}

/**********************************/

int main()
Expand Down Expand Up @@ -849,6 +893,7 @@ int main()
test7625();
test9785();
test9785_2();
test15207();

printf("Success\n");
return 0;
Expand Down