@@ -2660,45 +2660,6 @@ elem* toElem(Expression e, ref IRState irs)
26602660 return setResult2 (e);
26612661 }
26622662
2663- /* This will work if we can distinguish an assignment from
2664- * an initialization of the lvalue. It'll work if the latter.
2665- * If the former, because of aliasing of the return value with
2666- * function arguments, it'll fail.
2667- */
2668- if (ae.op == EXP .construct)
2669- {
2670- if (CallExp ce = lastComma(ae.e2).isCallExp())
2671- {
2672- TypeFunction tf = cast (TypeFunction)ce.e1.type.toBasetype();
2673- if (tf.ty == Tfunction && retStyle(tf, ce.f && ce.f.needThis()) == RET .stack)
2674- {
2675- elem* eh = el_una(OPaddr, TYnptr, e1);
2676- elem* e = toElemRVO(ae.e2, eh, irs);
2677- return setResult2 (e);
2678- }
2679-
2680- /* Look for:
2681- * v = structliteral.ctor(args)
2682- * and have the structliteral write into v, rather than create a temporary
2683- * and copy the temporary into v
2684- */
2685- if (ae.e1.op == EXP .variable && ce.e1.op == EXP .dotVariable)
2686- {
2687- auto dve = cast (DotVarExp)ce.e1;
2688- auto fd = dve.var.isFuncDeclaration();
2689- if (fd && fd.isCtorDeclaration())
2690- {
2691- if (auto sle = dve.e1.isStructLiteralExp())
2692- {
2693- elem* eh = el_una(OPaddr, TYnptr, e1);
2694- elem* e = toElemRVO(ae.e2, eh, irs);
2695- return setResult2 (e);
2696- }
2697- }
2698- }
2699- }
2700- }
2701-
27022663 // if (ae.op == EXP.construct) printf("construct\n");
27032664 if (auto t1s = t1b.isTypeStruct())
27042665 {
@@ -2764,6 +2725,15 @@ elem* toElem(Expression e, ref IRState irs)
27642725 }
27652726 }
27662727
2728+ /* Implement:
2729+ * S struct = func()
2730+ */
2731+ if (ae.op == EXP .construct && canElideCopy(ae.e2, ae.e1.type))
2732+ {
2733+ elem* e = toElemRVO(ae.e2, e1, irs);
2734+ return setResult2 (e);
2735+ }
2736+
27672737 /* Implement:
27682738 * (struct = struct)
27692739 */
@@ -2852,6 +2822,15 @@ elem* toElem(Expression e, ref IRState irs)
28522822 }
28532823 }
28542824
2825+ /* Implement:
2826+ * S[n] sarray = func()
2827+ */
2828+ if (ae.op == EXP .construct && canElideCopy(ae.e2, ae.e1.type))
2829+ {
2830+ elem* e = toElemRVO(ae.e2, e1, irs);
2831+ return setResult2 (e);
2832+ }
2833+
28552834 /* https://issues.dlang.org/show_bug.cgi?id=13661
28562835 * Even if the elements in rhs are all rvalues and
28572836 * don't have to call postblits, this assignment should call
@@ -4180,21 +4159,58 @@ elem* toElem(Expression e, ref IRState irs)
41804159 * e = Expression to convert
41814160 * ehidden = lvalue element to place the value in these forms:
41824161 * (OPvar TYnptr), which is a pointer to the storage
4183- * (OPvar TYsarray /TYstruct), which is the target symbol
4184- * (OPind TYsarray /TYstruct), which is the target location
4162+ * (OPvar TYarray /TYstruct), which is the target symbol
4163+ * (OPind TYarray /TYstruct), which is the target location
41854164 * irs = context
4165+ * offsetVar = the field to extract from e
41864166 * Returns:
4187- * generated elem tree
4167+ * generated elem tree with type TYarray/TYstruct
41884168 */
4189- elem* toElemRVO (Expression e, elem* ehidden, ref IRState irs)
4169+ elem* toElemRVO (Expression e, elem* ehidden, ref IRState irs, VarDeclaration offsetVar = null )
41904170{
4191- assert (e.type.toBasetype().ty == Tstruct || e.type.toBasetype().ty == Tsarray);
4171+ /* RVO is only applicable to structs and static arrays.
4172+ * When extracting a single field, also reject unions.
4173+ */
4174+ if (offsetVar)
4175+ {
4176+ assert (! offsetVar.overlapped);
4177+ assert (offsetVar.type.toBasetype().ty == Tstruct || offsetVar.type.toBasetype().ty == Tsarray);
4178+ }
4179+ else
4180+ {
4181+ assert (e.type.toBasetype().ty == Tstruct || e.type.toBasetype().ty == Tsarray);
4182+ }
4183+
4184+ /* There are cases where RVO appears to be possible in the frontend,
4185+ * but can't be done here. Generate a blit here and wait for bug reports,
4186+ * so frontend code can be fixed.
4187+ */
4188+ elem* blitToDestination (Expression e)
4189+ {
4190+ if (offsetVar)
4191+ {
4192+ e = new DotVarExp(e.loc, e, offsetVar, false );
4193+ e.type = offsetVar.type;
4194+ }
4195+
4196+ elem* e1 = toElem(e, irs);
4197+
4198+ if (tybasic(e1.Ety) == TYnoreturn)
4199+ return e1;
4200+
4201+ // Generate (ehidden=e)
4202+ if (tybasic(ehidden.Ety) == TYnptr)
4203+ ehidden = el_una(OPind, e1.Ety, ehidden);
4204+ elem* ea = elAssign(ehidden, e1, null , e1.ET );
4205+ elem_setLoc(ea, e.loc);
4206+ return ea;
4207+ }
41924208
41934209 elem* doCommaRVO (CommaExp ce)
41944210 {
41954211 assert (ce.e1 && ce.e2);
41964212 elem* eleft = toElem(ce.e1, irs);
4197- elem* eright = toElemRVO(ce.e2, ehidden, irs);
4213+ elem* eright = toElemRVO(ce.e2, ehidden, irs, offsetVar );
41984214 elem* e = el_combine(eleft, eright);
41994215 if (e)
42004216 elem_setLoc(e, ce.loc);
@@ -4207,15 +4223,15 @@ elem* toElemRVO(Expression e, elem* ehidden, ref IRState irs)
42074223 auto ctfecond = ce.econd.op == EXP .not ? (cast (NotExp)ce.econd).e1 : ce.econd;
42084224 if (auto ve = ctfecond.isVarExp())
42094225 if (ve.var && ve.var.ident == Id.ctfe)
4210- return toElemRVO (ctfecond is ce.econd ? ce.e2 : ce.e1, ehidden, irs);
4226+ return toElemRVO (ctfecond is ce.econd ? ce.e2 : ce.e1, ehidden, irs, offsetVar );
42114227
42124228 elem* ec = toElem(ce.econd, irs);
42134229
4214- elem* eleft = toElemRVO(ce.e1, ehidden, irs);
4230+ elem* eleft = toElemRVO(ce.e1, ehidden, irs, offsetVar );
42154231 if (irs.params.cov && ce.e1.loc.linnum)
42164232 eleft = el_combine(incUsageElem(irs, ce.e1.loc), eleft);
42174233
4218- elem* eright = toElemRVO(ce.e2, el_copytree(ehidden), irs);
4234+ elem* eright = toElemRVO(ce.e2, el_copytree(ehidden), irs, offsetVar );
42194235 if (irs.params.cov && ce.e2.loc.linnum)
42204236 eright = el_combine(incUsageElem(irs, ce.e2.loc), eright);
42214237
@@ -4235,17 +4251,127 @@ elem* toElemRVO(Expression e, elem* ehidden, ref IRState irs)
42354251
42364252 elem* doCallRVO (CallExp ce)
42374253 {
4238- return toElemCall (ce, irs, ehidden);
4254+ assert (! offsetVar);
4255+
4256+ FuncDeclaration fd;
4257+
4258+ if (auto dve = ce.e1.isDotVarExp())
4259+ fd = dve.var.isFuncDeclaration();
4260+ else
4261+ fd = ce.f;
4262+
4263+ Type t = ce.e1.type.toBasetype();
4264+ if (t.ty == Tdelegate)
4265+ t = t.nextOf();
4266+
4267+ if (fd && fd.isCtorDeclaration() ||
4268+ t.ty == Tfunction && retStyle(cast (TypeFunction)t, fd && fd.needThis()) == RET .stack)
4269+ {
4270+ // The hidden pointer must be a pointer by definition.
4271+ if (tybasic(ehidden.Ety) != TYnptr)
4272+ ehidden = el_una(OPaddr, TYnptr, ehidden);
4273+
4274+ return toElemCall (ce, irs, ehidden);
4275+ }
4276+
4277+ return blitToDestination (ce);
4278+ }
4279+
4280+ elem* doVariableRVO (VarExp ve)
4281+ {
4282+ assert (! offsetVar);
4283+
4284+ /* If ve.var is already pointing to the hidden pointer,
4285+ * replace it with ehidden.
4286+ */
4287+ if (ehidden.Eoper == OPvar &&
4288+ ehidden.Voffset == 0 &&
4289+ ehidden.Vsym == toSymbol(ve.var))
4290+ {
4291+ if (tybasic(ehidden.Ety) == TYnptr)
4292+ {
4293+ ehidden = el_una(OPind, totym(ve.var.type), ehidden);
4294+ ehidden.ET = Type_toCtype(ve.var.type);
4295+ }
4296+ return ehidden;
4297+ }
4298+
4299+ /* Blit otherwise. This means a frontend-generated variable
4300+ * fails to account for RVO.
4301+ */
4302+ return blitToDestination (ve);
4303+ }
4304+
4305+ elem* doDotVariableRVO (DotVarExp dve)
4306+ {
4307+ assert (! offsetVar);
4308+
4309+ if (auto vd = dve.var.isVarDeclaration())
4310+ if (! vd.overlapped)
4311+ return toElemRVO (dve.e1, ehidden, irs, vd);
4312+
4313+ return blitToDestination (dve);
42394314 }
42404315
42414316 elem* doStructLiteralRVO (StructLiteralExp sle)
42424317 {
4318+ if (offsetVar)
4319+ {
4320+ if (sle.useStaticInit)
4321+ {
4322+ // Generate (ehidden=*(&__init+offset))
4323+ elem* e1 = el_var(toInitializer(sle.sd));
4324+ e1 = addressElem(e1, sle.sd.type);
4325+ elem* eoffset = el_long(TYsize_t, offsetVar.offset);
4326+ e1 = el_bin(OPadd, TYnptr, e1, eoffset);
4327+ if (offsetVar.storage_class & (STC .out_ | STC .ref_))
4328+ e1 = el_una(OPind, TYnptr, e1);
4329+ e1 = el_una(OPind, totym(offsetVar.type), e1);
4330+ e1.ET = Type_toCtype(offsetVar.type);
4331+ if (tybasic(ehidden.Ety) == TYnptr)
4332+ ehidden = el_una(OPind, e1.Ety, ehidden);
4333+ elem* ea = elAssign(ehidden, e1, null , e1.ET );
4334+ elem_setLoc(ea, sle.loc);
4335+ return ea;
4336+ }
4337+
4338+ /* Find the corresponding initializer for offsetVar,
4339+ * and fold S(f(), g(), h()).field2 to g().
4340+ * Generate (f(), ehidden=g(), h(), ehidden).
4341+ */
4342+ elem* e1, ex;
4343+ bool match;
4344+
4345+ foreach (i, element; * sle.elements)
4346+ {
4347+ if (offsetVar == sle.sd.fields[i])
4348+ {
4349+ if (canElideCopy(element, element.type))
4350+ ex = toElemRVO(element, ehidden, irs);
4351+ else
4352+ ex = blitToDestination(element);
4353+ match = true ;
4354+ }
4355+ else
4356+ ex = toElem(element, irs);
4357+ e1 = el_combine(e1, ex);
4358+ }
4359+
4360+ assert (match);
4361+ ehidden = el_copytree(ehidden);
4362+ if (tybasic(ehidden.Ety) == TYnptr)
4363+ ehidden = el_una(OPind, e1.Ety, ehidden);
4364+ e1 = el_combine(e1, ehidden);
4365+ elem_setLoc(e1, sle.loc);
4366+ return e1;
4367+ }
4368+
42434369 if (ehidden.Eoper == OPvar && ehidden.Voffset == 0 )
42444370 return toElemStructLit (sle, irs, EXP .construct, ehidden.Vsym, true );
42454371
42464372 // Generate (tmp=&ehidden, *tmp=struct)
42474373 if (tybasic(ehidden.Ety) != TYnptr)
4248- ehidden = addressElem(ehidden, sle.sd.type.toBasetype() );
4374+ ehidden = addressElem(ehidden, sle.sd.type);
42494375 Symbol* stmp = symbol_genauto(TYnptr);
42504376 elem* e1 = el_bin(OPeq, TYnptr, el_var(stmp), ehidden);
42514377 elem* es = toElemStructLit(sle, irs, EXP .construct, stmp, true );
@@ -4254,14 +4380,54 @@ elem* toElemRVO(Expression e, elem* ehidden, ref IRState irs)
42544380 return e1;
42554381 }
42564382
4257- // Please keep in sync with dmd.expressionsem.canElideCopy()
4258- switch (e.op)
4383+ /* For field access, transform:
4384+ *
4385+ * [1] v = (exp1, cond ? exp2 : exp3).field
4386+ * => (exp1, cond ? (v = exp2.field) : (v = exp3.field))
4387+ *
4388+ * [2] v = S(f(), field: T()).field
4389+ * => (f(), v = T())
4390+ *
4391+ * TODO: Optimally, we should be able to optimize
4392+ * v = S(f(), v1: T(g(), v2: R()), h()).v1.v2
4393+ * => (f(), g(), v = R(), h(), v)
4394+ * but it would take extra complexity to keep evaluation order unchanged.
4395+ *
4396+ * For initialization, transform:
4397+ *
4398+ * [1] v = (exp1, cond ? exp2 : exp3)
4399+ * => (exp1, cond ? (v = exp2) : (v = exp3))
4400+ *
4401+ * [2] v = func()
4402+ * => pass the address of v to func(), if func() is ctor or returns on stack
4403+ *
4404+ * [3] v = v
4405+ * => v
4406+ *
4407+ * NOTE: Please keep in sync with dmd.expressionsem.canElideCopy().
4408+ */
4409+ if (offsetVar)
4410+ {
4411+ switch (e.op)
4412+ {
4413+ case EXP .comma: return doCommaRVO(e.isCommaExp());
4414+ case EXP .question: return doCondRVO(e.isCondExp());
4415+ case EXP .structLiteral: return doStructLiteralRVO(e.isStructLiteralExp());
4416+ default : return blitToDestination(e);
4417+ }
4418+ }
4419+ else
42594420 {
4260- case EXP .comma: return doCommaRVO(e.isCommaExp());
4261- case EXP .question: return doCondRVO(e.isCondExp());
4262- case EXP .call: return doCallRVO(e.isCallExp());
4263- case EXP .structLiteral: return doStructLiteralRVO(e.isStructLiteralExp());
4264- default : assert (0 );
4421+ switch (e.op)
4422+ {
4423+ case EXP .comma: return doCommaRVO(e.isCommaExp());
4424+ case EXP .question: return doCondRVO(e.isCondExp());
4425+ case EXP .call: return doCallRVO(e.isCallExp());
4426+ case EXP .variable: return doVariableRVO(e.isVarExp());
4427+ case EXP .dotVariable: return doDotVariableRVO(e.isDotVarExp());
4428+ case EXP .structLiteral: return doStructLiteralRVO(e.isStructLiteralExp());
4429+ default : assert (0 );
4430+ }
42654431 }
42664432}
42674433
0 commit comments