Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,10 @@ bool isLvalue(Expression _this)
* Determine if copy elision is allowed when copying an expression to
* a typed storage. This basically elides a restricted subset of so-called
* "pure" rvalues, i.e. expressions with no reference semantics.
*
* Note: Please try to keep `dmd.glue.e2ir.toElem()` in sync with this.
* It is not destructive to fail to elide a copy, but it is always better
* to stay consistent.
*/
bool canElideCopy(Expression e, Type to, bool checkMod = true)
{
Expand Down
98 changes: 91 additions & 7 deletions compiler/src/dmd/glue/e2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ elem* toElem(Expression e, ref IRState irs, elem* ehidden = null)
}
}

Symbol* s = toSymbol(se.var);
Symbol* s = toSymbolNRVO(se.var);

// VarExp generated for `__traits(initSymbol, Aggregate)`?
if (auto symDec = se.var.isSymbolDeclaration())
Expand All @@ -718,9 +718,7 @@ elem* toElem(Expression e, ref IRState irs, elem* ehidden = null)
if (se.var.toParent2())
fd = se.var.toParent2().isFuncDeclaration();

const bool nrvo = fd && (fd.isNRVO && fd.nrvo_var == se.var || se.var.nrvo && fd.shidden);
if (nrvo)
s = cast(Symbol*)fd.shidden;
const bool nrvo = fd && s == fd.shidden;

if (s.Sclass == SC.auto_ || s.Sclass == SC.parameter || s.Sclass == SC.shadowreg)
{
Expand Down Expand Up @@ -3185,15 +3183,17 @@ elem* toElem(Expression e, ref IRState irs, elem* ehidden = null)
auto ctfecond = ce.econd.op == EXP.not ? (cast(NotExp)ce.econd).e1 : ce.econd;
if (auto ve = ctfecond.isVarExp())
if (ve.var && ve.var.ident == Id.ctfe)
return toElem(ctfecond is ce.econd ? ce.e2 : ce.e1, irs);
return toElem(ctfecond is ce.econd ? ce.e2 : ce.e1, irs, ehidden);

elem* ec = toElem(ce.econd, irs);

elem* eleft = toElem(ce.e1, irs);
elem* eleft = toElem(ce.e1, irs, ehidden);
if (irs.params.cov && ce.e1.loc.linnum)
eleft = el_combine(incUsageElem(irs, ce.e1.loc), eleft);

elem* eright = toElem(ce.e2, irs);
if (ehidden)
ehidden = el_copytree(ehidden);
elem* eright = toElem(ce.e2, irs, ehidden);
if (irs.params.cov && ce.e2.loc.linnum)
eright = el_combine(incUsageElem(irs, ce.e2.loc), eright);

Expand Down Expand Up @@ -4226,6 +4226,90 @@ elem* toElem(Expression e, ref IRState irs, elem* ehidden = null)
return el_ptr(toSymbol(e));
}

/*****************************************************/
/* RVO special cases */
/*****************************************************/

elem* moveToHiddenPtr(Expression e)
{
elem* el = toElem(e, irs);
elem* ea = elAssign(el_una(OPind, el.Ety, ehidden), el, e.type, null);
elem* eh = el_una(OPind, el.Ety, el_copytree(ehidden));
eh.ET = el.ET;
return el_combine(ea, eh);
}

elem* doCallRVO(CallExp e)
{
FuncDeclaration fd;

if (auto dve = e.e1.isDotVarExp())
{
fd = dve.var.isFuncDeclaration();
auto sle = dve.e1.isStructLiteralExp();
if (fd && fd.isCtorDeclaration() && sle)
{
assert(ehidden.Eoper == OPvar);
sle.sym = ehidden.Vsym;
return toElem(e, irs);
}
}
else
{
fd = e.f;
}

// If fd is eligible for RVO, invoke visitCall directly to pick up ehidden
Type t = e.e1.type.toBasetype();
if (t.ty == Tdelegate)
t = t.nextOf();
if (t.ty == Tfunction && retStyle(cast(TypeFunction)t, fd && fd.needThis()) == RET.stack)
return visitCall(e);

// The return value is passed in registers
return moveToHiddenPtr(e);
}

elem* doVariableRVO(VarExp e)
{
// Replace e with ehidden if e.var is already the hidden variable
if (ehidden.Eoper == OPvar && ehidden.Vsym == toSymbolNRVO(e.var))
{
elem* el = toElem(e, irs);
elem* eh = el_una(OPind, el.Ety, ehidden);
eh.ET = el.ET;
el_free(el);
return eh;
}

// Otherwise a move is inevitable
return moveToHiddenPtr(e);
}

elem* doStructLiteralRVO(StructLiteralExp e)
{
// Patch e.sym and proceed as if there is no RVO
assert(ehidden.Eoper == OPvar);
e.sym = ehidden.Vsym;
return toElem(e, irs);
}

if (ehidden)
{
// Catch unhandled RVO cases
// Please keep in sync with dmd.expressionsem.canElideCopy()
switch (e.op)
{
case EXP.comma: return visitComma(e.isCommaExp());
case EXP.question: return visitCond(e.isCondExp());
case EXP.call: return doCallRVO(e.isCallExp());
case EXP.variable: return doVariableRVO(e.isVarExp());
case EXP.dotVariable: return moveToHiddenPtr(e);
case EXP.structLiteral: return doStructLiteralRVO(e.isStructLiteralExp());
default: assert(0);
}
}

switch (e.op)
{
default: return visit(e);
Expand Down
146 changes: 52 additions & 94 deletions compiler/src/dmd/glue/s2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import dmd.dsymbol;
import dmd.dstruct;
import dmd.dtemplate;
import dmd.expression;
import dmd.expressionsem : getDsymbol, toInteger;
import dmd.expressionsem : canElideCopy, getDsymbol, toInteger;
import dmd.func;
import dmd.id;
import dmd.init;
Expand Down Expand Up @@ -538,143 +538,101 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
void visitReturn(ReturnStatement s)
{
//printf("s2ir.ReturnStatement: %s\n", toChars(s.exp));
BlockState* blx = irs.blx;
BC bc;

incUsage(irs, s.loc);
void finish()
void finish(elem* e = null)
{
block* finallyBlock;
if (config.ehmethod != EHmethod.EH_DWARF &&
!irs.isNothrow() &&
(finallyBlock = stmtstate.getFinallyBlock()) != null)
BC bc = BC.ret;
BlockState* blx = irs.blx;

if (e)
{
assert(finallyBlock.bc == BC._finally);
blx.curblock.appendSucc(finallyBlock);
elem_setLoc(e, s.loc);
block_appendexp(blx.curblock, e);
bc = BC.retexp;
}

if (config.ehmethod != EHmethod.EH_DWARF &&!irs.isNothrow())
{
if (block* finallyBlock = stmtstate.getFinallyBlock())
{
assert(finallyBlock.bc == BC._finally);
blx.curblock.appendSucc(finallyBlock);
}
}

block_setLoc(blx.curblock, s.loc);
block_next(blx, bc, null);
}

incUsage(irs, s.loc);

if (!s.exp)
{
bc = BC.ret;
return finish();
}

elem* e;

FuncDeclaration func = irs.getFunc();
assert(func);
auto tf = func.type.isTypeFunction();
assert(tf);

RET retmethod = retStyle(tf, func.needThis());
if (retmethod == RET.stack)
if (retStyle(tf, func.needThis()) == RET.stack)
{
elem* es;
bool writetohp;
bool nrvo = func.isNRVO && func.nrvo_var;
bool urvo = !nrvo && canElideCopy(s.exp, s.exp.type, false);

/* If returning struct literal, write result
* directly into return value
*/
if (auto sle = s.exp.isStructLiteralExp())
{
sle.sym = irs.shidden;
writetohp = true;
}
/* Detect function call that returns the same struct
* and construct directly into *shidden
*/
else if (auto ce = s.exp.isCallExp())
{
if (ce.e1.op == EXP.variable || ce.e1.op == EXP.star)
{
Type t = ce.e1.type.toBasetype();
if (t.ty == Tdelegate)
t = t.nextOf();
if (t.ty == Tfunction && retStyle(cast(TypeFunction)t, ce.f && ce.f.needThis()) == RET.stack)
{
e = toElemDtor(s.exp, irs, el_var(irs.shidden));
e = el_una(OPaddr, TYnptr, e);
goto L1;
}
}
else if (auto dve = ce.e1.isDotVarExp())
{
auto fd = dve.var.isFuncDeclaration();
if (fd && fd.isCtorDeclaration())
{
if (auto sle = dve.e1.isStructLiteralExp())
{
sle.sym = irs.shidden;
writetohp = true;
}
}
Type t = ce.e1.type.toBasetype();
if (t.ty == Tdelegate)
t = t.nextOf();
if (t.ty == Tfunction && retStyle(cast(TypeFunction)t, fd && fd.needThis()) == RET.stack)
{
e = toElemDtor(s.exp, irs, el_var(irs.shidden));
e = el_una(OPaddr, TYnptr, e);
goto L1;
}
}
}
e = toElemDtor(s.exp, irs);
// Pass shidden in ehidden for URVO
elem* ehidden = urvo ? el_var(irs.shidden) : null;
elem* e = toElemDtor(s.exp, irs, ehidden);
assert(e);

if (writetohp ||
(func.isNRVO && func.nrvo_var))
if (urvo)
{
// In URVO cases, toElemDtor already ensures e references
// the hidden pointer, so there is no need to rewrite
e = el_una(OPaddr, TYnptr, e);
}
else if (nrvo)
{
// Return value via hidden pointer passed as parameter
// Write exp; return shidden;
es = e;
// Rewrite to (exp, shidden)
e = el_combine(e, el_var(irs.shidden));
}
else
{
// Return value via hidden pointer passed as parameter
// Write *shidden=exp; return shidden;
es = el_una(OPind,e.Ety,el_var(irs.shidden));
// Rewrite to (*shidden=exp, shidden)
elem* es = el_una(OPind, e.Ety, el_var(irs.shidden));
es = elAssign(es, e, s.exp.type, null);
e = el_combine(es, el_var(irs.shidden));
}
e = el_var(irs.shidden);
e = el_bin(OPcomma, e.Ety, es, e);

return finish(e);
}
else if (tf.isRef)
{
// Reference return, so convert to a pointer
e = toElemDtor(s.exp, irs);

elem* e = toElemDtor(s.exp, irs);
assert(e);

if (tf.isRef)
{
/* already taken care of for vresult in buildResultVar() and semantic3.d
* https://issues.dlang.org/show_bug.cgi?id=19384
*/
if (func.vresult)
{
if (BlitExp be = s.exp.isBlitExp())
{
if (VarExp ve = be.e1.isVarExp())
{
if (ve.var == func.vresult)
goto Lskip;
return finish(e);
}
}
}

// Reference return, so convert to a pointer
e = addressElem(e, s.exp.type.pointerTo());
Lskip:
}
else
{
e = toElemDtor(s.exp, irs);
assert(e);
}
L1:
elem_setLoc(e, s.loc);
block_appendexp(blx.curblock, e);
bc = BC.retexp;
// if (type_zeroCopy(Type_toCtype(s.exp.type)))
// bc = BC.ret;
finish();

finish(e);
}

/**************************************
Expand Down
22 changes: 22 additions & 0 deletions compiler/src/dmd/glue/tocsym.d
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Symbol* toSymbolX(Dsymbol ds, const(char)* prefix, SC sclass, type* t, const(cha
}

/*************************************
* Convert D symbol to backend symbol.
*/

Symbol* toSymbol(Dsymbol s)
Expand Down Expand Up @@ -550,6 +551,27 @@ Symbol* toSymbol(Dsymbol s)
return v.result;
}

/*************************************
* Convert D symbol to backend symbol.
* Unlike toSymbol, this function also resolves
* NRVO variables to the hidden pointer.
*/
Symbol* toSymbolNRVO(Dsymbol s)
{
if (auto parent = s.toParent2())
{
auto fd = parent.isFuncDeclaration();
auto var = s.isVarDeclaration();
bool nrvo = fd && var && (fd.isNRVO && fd.nrvo_var == var || var.nrvo && fd.shidden);

if (nrvo)
{
return cast(Symbol*)fd.shidden;
}
}

return toSymbol(s);
}

/*************************************
* Create Windows import symbol from backend Symbol.
Expand Down
Loading
Loading