Skip to content

Commit 10859b8

Browse files
limepoutinethewilsonator
authored andcommitted
Allow more copy elision on return statements
1 parent 43875fe commit 10859b8

4 files changed

Lines changed: 92 additions & 85 deletions

File tree

compiler/src/dmd/glue/e2ir.d

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ elem* toElem(Expression e, ref IRState irs)
754754
}
755755
}
756756

757-
Symbol* s = toSymbol(se.var);
757+
Symbol* s = toSymbolNRVO(se.var);
758758

759759
// VarExp generated for `__traits(initSymbol, Aggregate)`?
760760
if (auto symDec = se.var.isSymbolDeclaration())
@@ -781,9 +781,7 @@ elem* toElem(Expression e, ref IRState irs)
781781
if (se.var.toParent2())
782782
fd = se.var.toParent2().isFuncDeclaration();
783783

784-
const bool nrvo = fd && (fd.isNRVO && fd.nrvo_var == se.var || se.var.nrvo && fd.shidden);
785-
if (nrvo)
786-
s = cast(Symbol*)fd.shidden;
784+
const bool nrvo = fd && s == fd.shidden;
787785

788786
if (s.Sclass == SC.auto_ || s.Sclass == SC.parameter || s.Sclass == SC.shadowreg)
789787
{
@@ -4419,7 +4417,7 @@ elem* toElemRVO(Expression e, elem* ehidden, ref IRState irs, Type forceType = n
44194417
* replace it with ehidden.
44204418
*/
44214419
if (ehidden.Eoper == OPvar && ehidden.Voffset == 0 &&
4422-
ehidden.Vsym == toSymbol(ve.var))
4420+
ehidden.Vsym == toSymbolNRVO(ve.var))
44234421
{
44244422
if (tybasic(ehidden.Ety) == TYnptr)
44254423
{

compiler/src/dmd/glue/s2ir.d

Lines changed: 32 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import dmd.dsymbol;
3838
import dmd.dstruct;
3939
import dmd.dtemplate;
4040
import dmd.expression;
41-
import dmd.expressionsem : getDsymbol, toInteger;
41+
import dmd.expressionsem : canElideCopy, getDsymbol, toInteger;
4242
import dmd.func;
4343
import dmd.id;
4444
import dmd.init;
@@ -559,29 +559,34 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
559559
void visitReturn(ReturnStatement s)
560560
{
561561
//printf("s2ir.ReturnStatement: %s\n", toChars(s.exp));
562-
BlockState* blx = irs.blx;
563-
BC bc;
564562

565563
incUsage(irs, s.loc);
566-
void finish()
564+
void finish(elem* e = null)
567565
{
568-
block* finallyBlock;
569-
if (config.ehmethod != EHmethod.EH_DWARF &&
570-
!irs.isNothrow() &&
571-
(finallyBlock = stmtstate.getFinallyBlock()) != null)
566+
BC bc = BC.ret;
567+
BlockState* blx = irs.blx;
568+
569+
if (e)
572570
{
573-
assert(finallyBlock.bc == BC.finally_);
574-
blx.curblock.Bsucc.push(finallyBlock);
571+
elem_setLoc(e, s.loc);
572+
block_appendexp(blx.curblock, e);
573+
bc = BC.retexp;
574+
}
575+
576+
if (config.ehmethod != EHmethod.EH_DWARF && !irs.isNothrow())
577+
{
578+
if (block* finallyBlock = stmtstate.getFinallyBlock())
579+
{
580+
assert(finallyBlock.bc == BC.finally_);
581+
blx.curblock.Bsucc.push(finallyBlock);
582+
}
575583
}
576584

577585
block_setLoc(blx.curblock, s.loc);
578586
block_next(blx, bc, null);
579587
}
580588
if (!s.exp)
581-
{
582-
bc = BC.ret;
583589
return finish();
584-
}
585590

586591
elem* e;
587592

@@ -590,78 +595,30 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
590595
auto tf = func.type.isTypeFunction();
591596
assert(tf);
592597

593-
RET retmethod = retStyle(tf, func.needThis());
594-
if (retmethod == RET.stack)
598+
if (retStyle(tf, func.needThis()) == RET.stack)
595599
{
596-
elem* es;
597-
bool writetohp;
600+
bool nrvo = func.isNRVO && func.nrvo_var;
601+
bool urvo = !nrvo && canElideCopy(s.exp, s.exp.type);
598602

599-
/* If returning struct literal, write result
600-
* directly into return value
601-
*/
602-
if (auto sle = s.exp.isStructLiteralExp())
603-
{
604-
sle.sym = irs.shidden;
605-
writetohp = true;
606-
}
607-
/* Detect function call that returns the same struct
608-
* and construct directly into *shidden
609-
*/
610-
else if (auto ce = s.exp.isCallExp())
611-
{
612-
if (ce.e1.op == EXP.variable || ce.e1.op == EXP.star)
613-
{
614-
Type t = ce.e1.type.toBasetype();
615-
if (t.ty == Tdelegate)
616-
t = t.nextOf();
617-
if (t.ty == Tfunction && retStyle(cast(TypeFunction)t, ce.f && ce.f.needThis()) == RET.stack)
618-
{
619-
e = toElemDtor(s.exp, irs, el_var(irs.shidden));
620-
e = el_una(OPaddr, TYnptr, e);
621-
goto L1;
622-
}
623-
}
624-
else if (auto dve = ce.e1.isDotVarExp())
625-
{
626-
auto fd = dve.var.isFuncDeclaration();
627-
if (fd && fd.isCtorDeclaration())
628-
{
629-
if (auto sle = dve.e1.isStructLiteralExp())
630-
{
631-
sle.sym = irs.shidden;
632-
writetohp = true;
633-
}
634-
}
635-
Type t = ce.e1.type.toBasetype();
636-
if (t.ty == Tdelegate)
637-
t = t.nextOf();
638-
if (t.ty == Tfunction && retStyle(cast(TypeFunction)t, fd && fd.needThis()) == RET.stack)
639-
{
640-
e = toElemDtor(s.exp, irs, el_var(irs.shidden));
641-
e = el_una(OPaddr, TYnptr, e);
642-
goto L1;
643-
}
644-
}
645-
}
646-
e = toElemDtor(s.exp, irs);
603+
// Pass shidden in ehidden for URVO
604+
elem* ehidden = urvo ? el_var(irs.shidden) : null;
605+
e = toElemDtor(s.exp, irs, ehidden);
647606
assert(e);
648607

649-
if (writetohp ||
650-
(func.isNRVO && func.nrvo_var))
608+
if (nrvo || urvo)
651609
{
652-
// Return value via hidden pointer passed as parameter
653-
// Write exp; return shidden;
654-
es = e;
610+
// For URVO, toElemDtor already returns the hidden pointer
611+
// so there is no need to rewrite
612+
e = el_una(OPaddr, TYnptr, e);
655613
}
656614
else
657615
{
658616
// Return value via hidden pointer passed as parameter
659617
// Write *shidden=exp; return shidden;
660-
es = el_una(OPind,e.Ety,el_var(irs.shidden));
618+
elem* es = el_una(OPind, e.Ety, el_var(irs.shidden));
661619
es = elAssign(es, e, s.exp.type, null);
620+
e = el_combine(es, el_var(irs.shidden));
662621
}
663-
e = el_var(irs.shidden);
664-
e = el_bin(OPcomma, e.Ety, es, e);
665622
}
666623
else if (tf.isRef)
667624
{
@@ -674,13 +631,8 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
674631
e = toElemDtor(s.exp, irs);
675632
assert(e);
676633
}
677-
L1:
678-
elem_setLoc(e, s.loc);
679-
block_appendexp(blx.curblock, e);
680-
bc = BC.retexp;
681-
// if (type_zeroCopy(Type_toCtype(s.exp.type)))
682-
// bc = BC.ret;
683-
finish();
634+
635+
finish(e);
684636
}
685637

686638
/**************************************

compiler/src/dmd/glue/tocsym.d

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ Symbol* toSymbol(Type t)
179179
}
180180

181181
/*************************************
182+
* Turn a D symbol into a C Symbol.
183+
* Params:
184+
* s = D symbol
185+
* Returns:
186+
* corresponding Symbol
182187
*/
183188
package(dmd.glue)
184189
Symbol* toSymbol(Dsymbol s)
@@ -647,6 +652,44 @@ Symbol* toSymbol(Dsymbol s)
647652
return v.result;
648653
}
649654

655+
/*************************************
656+
* Turn a D symbol into a C Symbol, but
657+
* also substitute NRVO variables for the
658+
* hidden symbol along the way.
659+
*
660+
* Params:
661+
* s = D symbol
662+
* Returns:
663+
* corresponding Symbol
664+
*/
665+
package(dmd.glue)
666+
Symbol* toSymbolNRVO(Dsymbol s)
667+
{
668+
if (auto parent = s.toParent2())
669+
{
670+
auto fd = parent.isFuncDeclaration();
671+
auto var = s.isVarDeclaration();
672+
673+
if (fd && var &&
674+
(fd.isNRVO && fd.nrvo_var == var ||
675+
fd.shidden && var.nrvo))
676+
{
677+
auto shidden = cast(Symbol*)fd.shidden;
678+
679+
/* Nested function accessing NRVO variable.
680+
* Consider the variable volatile in the same way
681+
* other variables with nested ref do.
682+
*/
683+
if (var.nestedrefs.length)
684+
type_setcv(&shidden.Stype, shidden.Stype.Tty | mTYvolatile);
685+
686+
return shidden;
687+
}
688+
}
689+
690+
return toSymbol(s);
691+
}
692+
650693

651694
/*********************************
652695
* Generate import symbol from symbol.

compiler/test/runnable/nrvo.d

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ out(v; v.ptr == &v)
8989
return b3 ? make3() : f3();
9090
}
9191

92+
S3 h3()
93+
{
94+
static S3 lazyS3(lazy S3 s)
95+
{
96+
return s();
97+
}
98+
99+
return b3 ? assert(0) : lazyS3(f3());
100+
}
101+
92102
void test3()
93103
{
94104
S3 s1 = f3();
@@ -121,6 +131,10 @@ void test3()
121131
}
122132

123133
f3().b.check();
134+
135+
S3 s5 = h3();
136+
s5.check();
137+
assert(i3 == 5);
124138
}
125139

126140
/***************************************************/

0 commit comments

Comments
 (0)