This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
Re: C++17 std::launder and aliasing
On Mon, 24 Oct 2016, Jonathan Wakely wrote:
> On 24/10/16 10:18 +0200, Richard Biener wrote:
> > On Mon, 24 Oct 2016, Ville Voutilainen wrote:
> >
> > > On 24 October 2016 at 10:41, Richard Biener <rguenther@suse.de> wrote:
> > > >> Seems that testcase is devirtualization related.
> > > >> With -O3 -fno-devirtualize we return 3 (but still no calls).
> > > >> With asm ("" : "+g" (p)); before return p; in launder we don't
> > > devirtualize
> > > >> it any more and return 3 in the end. Is the testcase only valid
> > > >> with std::launder and not valid otherwise (I hope so, otherwise we are
> > > in
> > > >> big trouble with devirtualization)?
> > > >
> > > > I believe the testcase shows a bug in devirtualization. (stpuid
> > > > godbolt.org having no way to textually extract the source easily)
> > > >
> > > > Please file a GCC bugreport. GCC shouldn't miscompile this even
> > > > without std::launder.
> > >
> > >
> > > Note that A::f() can be in a different translation unit, so we still
> > > need the optimization
> > > barrier in some cases, even if in this particular case as written we
> > > wouldn't.
> >
> > Well, if it is valid to construct a new object in place of *this in
> > a virtual function call on this then GCC has an issue regardless of
> > std::launder. So, for
> >
> > {
> > A a;
> > A::f ();
> > }
> >
> > where does the _FE_ get the knowledge what destructor to call? IIRC
> > we "devirtualize" destructor calls in this case as well.
> >
> > struct B {
> > virtual ~B();
> > };
> > struct A : B {
> > virtual ~A();
> > void foo ();
> > };
> >
> > int foo ()
> > {
> > A a;
> > a.foo();
> > }
> >
> >
> > foo ends up calling A::~A() with GCC 6.
> >
> > How do I launder a here?
>
> This example has undefined behaviour even if you add std::launder. The
> original example doesn't.
>
> The difference is 3.8 [basic.life] paragraph 9, which says that if the
> program ends the lifetime of the automatic object 'a' then it must
> ensure that another object of the same type occupies the same storage
> when the implicit destructor runs. So the original example is OK
> because it ends the lifetime of the original object by creating a B at
> the same location, but then creates another A there, so the implicit
> destructor call to A::~A() is valid, because there is an A at that
> location.
Ok, but in the original example if either f () would throw
before switching back to A then the testcase would invoke
undefined behavior again?
> I don't think we have a bug, or a defect, in the absence of
> std::launder. Without std::launder our devirtualization is OK.
>
> Again, std::launder(&a) is not the same as &a, so if we simply fold
> std::launder(&a) to &a we allow devirtualization through the pointer
> as though it was still a pointer to the original object &a, and that's
> what std::launder is supposed to prevent.
Note that std::launder () then has to be implemented as clobbering
memory (the vtable pointer) as otherwise the devirt machiner won't see it.
This means that std::launder () will pessimize optimizations (well,
we can try to special-case it in the alias machinery to only consider
clobbering its argument directly pointed to memory and not keeping
its argument pointed to memory live).
Richard.