Hi, The D front-end semantic pass sometimes declares a temporary inside a return expression. This is now detected with the RESULT_DECL replacing the temporary, allowing for RVO to be done.
Patch regression tested on x86_64-linux-gnu/-m32/-mx32, committed to mainline and backported to releases/gcc-11 branch. Regards Iain --- PR d/101273 gcc/d/ChangeLog: * toir.cc (IRVisitor::visit (ReturnStatement *)): Detect returns that use a temporary, and replace with return value. gcc/testsuite/ChangeLog: * gdc.dg/torture/pr101273.d: New test. --- gcc/d/toir.cc | 32 ++++++++++++++++++-- gcc/testsuite/gdc.dg/torture/pr101273.d | 39 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gdc.dg/torture/pr101273.d diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index 41d07a7b70e..eaee6f7e803 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -1034,14 +1034,37 @@ public: /* Detect a call to a constructor function, or if returning a struct literal, write result directly into the return value. */ StructLiteralExp *sle = NULL; + bool using_rvo_p = false; if (DotVarExp *dve = (s->exp->op == TOKcall && s->exp->isCallExp ()->e1->op == TOKdotvar ? s->exp->isCallExp ()->e1->isDotVarExp () : NULL)) { - sle = (dve->var->isCtorDeclaration () - ? dve->e1->isStructLiteralExp () : NULL); + if (dve->var->isCtorDeclaration ()) + { + if (CommaExp *ce = dve->e1->isCommaExp ()) + { + /* Temporary initialized inside a return expression, and + used as the return value. Replace it with the hidden + reference to allow RVO return. */ + DeclarationExp *de = ce->e1->isDeclarationExp (); + VarExp *ve = ce->e2->isVarExp (); + if (de != NULL && ve != NULL + && ve->var == de->declaration + && ve->var->storage_class & STCtemp) + { + tree var = get_symbol_decl (ve->var); + TREE_ADDRESSABLE (var) = 1; + SET_DECL_VALUE_EXPR (var, decl); + DECL_HAS_VALUE_EXPR_P (var) = 1; + SET_DECL_LANG_NRVO (var, this->func_->shidden); + using_rvo_p = true; + } + } + else + sle = dve->e1->isStructLiteralExp (); + } } else sle = s->exp->isStructLiteralExp (); @@ -1050,11 +1073,16 @@ public: { StructDeclaration *sd = type->baseElemOf ()->isTypeStruct ()->sym; sle->sym = build_address (this->func_->shidden); + using_rvo_p = true; /* Fill any alignment holes in the return slot using memset. */ if (!identity_compare_p (sd) || sd->isUnionDeclaration ()) add_stmt (build_memset_call (this->func_->shidden)); + } + if (using_rvo_p == true) + { + /* Generate: (expr, return <retval>); */ add_stmt (build_expr_dtor (s->exp)); } else diff --git a/gcc/testsuite/gdc.dg/torture/pr101273.d b/gcc/testsuite/gdc.dg/torture/pr101273.d new file mode 100644 index 00000000000..e300e03f199 --- /dev/null +++ b/gcc/testsuite/gdc.dg/torture/pr101273.d @@ -0,0 +1,39 @@ +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101273 +// { dg-do run } + +struct S101273 +{ + int x; + S101273* impl; + this(int x) + { + this.x = x; + this.impl = &this; + } + ~this() { } +} + +S101273 makeS101273() +{ + return S101273(2); +} + +S101273 nrvo101273() +{ + S101273 ret = makeS101273(); + return ret; +} + +S101273 rvo101273() +{ + return makeS101273(); +} + +void main() +{ + auto nrvo = nrvo101273(); + assert(&nrvo is nrvo.impl); + + auto rvo = rvo101273(); + assert(&rvo is rvo.impl); +} -- 2.30.2