From 46d1cb4c218ff1fb73b391a28218ee1f362e8ca1 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sat, 3 Jul 2021 02:42:14 +0200 Subject: [PATCH] d: Missed RVO optimization with non-POD structs 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. 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. (cherry picked from commit 152f4d0e4d3b524ce30d05f20e23a44b0dd29765) --- 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 41d07a7b70e5..eaee6f7e8039 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 ); */ 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 000000000000..e300e03f1997 --- /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.47.2