d: Missed RVO optimization with non-POD structs
authorIain Buclaw <ibuclaw@gdcproject.org>
Sat, 3 Jul 2021 00:42:14 +0000 (02:42 +0200)
committerIain Buclaw <ibuclaw@gdcproject.org>
Sat, 3 Jul 2021 11:00:56 +0000 (13:00 +0200)
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.

gcc/d/toir.cc
gcc/testsuite/gdc.dg/torture/pr101273.d [new file with mode: 0644]

index 41d07a7..eaee6f7 100644 (file)
@@ -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 (file)
index 0000000..e300e03
--- /dev/null
@@ -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);
+}