Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dmd-cxx] Move check for NRVO from ReturnStatement semantic into FuncDeclaration #11622

Merged
merged 1 commit into from
Aug 25, 2020

Conversation

ibuclaw
Copy link
Member

@ibuclaw ibuclaw commented Aug 25, 2020

Backport of #8467. This is a companion patch for NRVO related fixes in pr d/96156 and d/96157.

@ibuclaw ibuclaw added GDC Gnu D Compiler C++ Port labels Aug 25, 2020
@dlang-bot
Copy link
Contributor

Thanks for your pull request, @ibuclaw!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "dmd-cxx + dmd#11622"

Copy link
Member

@Geod24 Geod24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! There are still a few checks in the backend though, do you plan to move those too ?

@dlang-bot dlang-bot merged commit fe5f388 into dlang:dmd-cxx Aug 25, 2020
@ibuclaw ibuclaw deleted the dmd-cxx-checknrvo branch August 25, 2020 07:34
@ibuclaw
Copy link
Member Author

ibuclaw commented Aug 25, 2020

Nice! There are still a few checks in the backend though, do you plan to move those too ?

There are a few common ABI optimizations at play here.

  1. Fully addressable/non-trivial types: All copies of an object are enforced by front-end language semantics (back-end/optimizer cannot do anything that causes a copy operation). These are always passed and returned by reference.
  2. Call slot optimization (or RVO): A type that must return in memory by ABI of the target (e.g: because it doesn't fit in registers) becomes the first parameter of a function, which is used as the return value.
  3. Direct construction at return address: Returning a literal that has a constructor usually creates a temporary for the 'this' pointer. If the type is returning in memory anyway, code generator can elide the temporary and pass the return address to the constructor instead.

NRVO is just applied on top of 1 or 2 by binding a variable name to the result decl.

Of the above, [1] is already managed by the front-end, though changing types to be ref is handled by the code generator. [2] is target specific, and an optimization, so out of scope of what the language should guarantee. [3] The front-end does not have the notion of a result declaration (return expr; is really return <result> = expr), nor does the front-end create such temporaries for literals whose address needs to be taken (AST looks like return S().__ctor(1, 2, 3)) when we really generate return <result> = S(), __ctor(&<result> 1, 2, 3)).

@ibuclaw
Copy link
Member Author

ibuclaw commented Aug 25, 2020

Which is a long winded way of say: While there could be some improvements in hinting what the code generator should be generating, rather than second-guessing the AST. So long as this feature is only an optimization, there's no desire to put new rules to enforce behaviour in the front-end.

@Geod24
Copy link
Member

Geod24 commented Aug 25, 2020

The underlying reason for my question is: https://issues.dlang.org/show_bug.cgi?id=20752
At the moment using non-copy-able types is still a pain (and those types aren't really non-copy-able).

@ibuclaw
Copy link
Member Author

ibuclaw commented Aug 25, 2020

isReturnOnStack is correct (hence no static assert), but it does not mean that all copied would be elided along the way. That's what @disable is meant for.

@Geod24
Copy link
Member

Geod24 commented Aug 25, 2020

but it does not mean that all copied would be elided along the way

AFAIR, that's not what is happening here. I think it might be:

dmd/src/dmd/toir.d

Lines 737 to 743 in c15a596

/* Can't do nrvo if the variable is put in a closure, since
* what the shidden points to may no longer exist.
*/
if (fd.nrvo_can && fd.nrvo_var == v)
{
fd.nrvo_can = false;
}

That's what @disable is meant for.

There are (documented) cases where DMD flat out ignore @disable and does the move anyway.

@ibuclaw
Copy link
Member Author

ibuclaw commented Aug 25, 2020

AFAIR, that's not what is happening here. I think it might be:

Maybe, but both gdc and ldc should be mimicing that behaviour anyway. (On another note, I don't think there's any specific test that is designed to hit that code path).

There are (documented) cases where DMD flat out ignore @disable and does the move anyway.

Would be interesting to see them tried with gdc. If there's any violations then an ICE would occur. If no ICE occurs and you still get a bad program, then the problem is in the front-end, not the code generator.

kraj pushed a commit to kraj/gcc that referenced this pull request Aug 26, 2020
…ctor.

Backports a change from upstream dmd that moves front-end NRVO checking
from ReturnStatement semantic to the end of FuncDeclaration semantic.

In the codegen, retStyle has been partially implemented so that only
structs and static arrays return RETstack.  This isn't accurate, but
don't need to be for the purposes of semantic analysis.

If a function either has TREE_ADDRESSABLE or must return in memory, then
DECL_RESULT is set as the shidden field for the function.  This is used
in the codegen pass for ReturnStatement where it is now detected whether
a function is returning a struct literal or a constructor function, then
the DECL_RESULT is used to directly construct the return value, instead
of doing so via temporaries.

Reviewed-on: dlang/dmd#11622

gcc/d/ChangeLog:

	PR d/96156
	* d-frontend.cc (retStyle): Only return RETstack for struct and static
	array types.
	* decl.cc (DeclVisitor::visit (FuncDeclaration *)): Use NRVO return
	for all TREE_ADDRESSABLE types.  Set shidden to the RESULT_DECL.
	* expr.cc (ExprVisitor::visit (CallExp *)): Force TARGET_EXPR if the
	'this' pointer reference is a CONSTRUCTOR.
	(ExprVisitor::visit (StructLiteralExp *)): Generate assignment to the
	symbol to initialize with literal.
	* toir.cc (IRVisitor::visit (ReturnStatement *)): Detect returning
	struct literals and write directly into the RESULT_DECL.
	* dmd/MERGE: Merge upstream dmd fe5f388d8.

gcc/testsuite/ChangeLog:

	PR d/96156
	* gdc.dg/pr96156.d: New test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants