[cxx-abi-dev] Guaranteed copy elision and tail padding

John McCall rjmccall at apple.com
Thu Jul 21 20:57:05 UTC 2016


> On Jul 21, 2016, at 1:31 PM, Richard Smith <richardsmith at googlers.com> wrote:
> On 21 July 2016 at 13:20, Jason Merrill <jason at redhat.com <mailto:jason at redhat.com>> wrote:
> On Thu, Jul 21, 2016 at 2:45 PM, Richard Smith
> <richardsmith at googlers.com <mailto:richardsmith at googlers.com>> wrote:
> > On 21 July 2016 at 11:02, Jason Merrill <jason at redhat.com <mailto:jason at redhat.com>> wrote:
> >>
> >> P0135 seems to require that we elide the copy when using the result of
> >> a function returning by value to initialize a base class subobject,
> >> but the ABI doesn't currently require that such a function avoid
> >> clobbering tail padding when initializing its return object.
> >> Thoughts?
> >
> > If the function clobbers the tail padding of its return object, at least GCC
> > and Clang will miscompile the program today, without P0135:
> >
> > #include <string.h>
> > struct X { ~X() {} int n; char d; };
> > struct Y { Y(); char c[3]; };
> > struct Z : X, virtual Y { Z(); };
> >
> > X f() { X nrvo; memset(&nrvo, 0, sizeof(X)); return nrvo; }
> > Z::Z() : Y(), X(f()) {}
> > Y::Y() : c{1, 2, 3} {}
> >
> > int main() {
> >   Z z;
> >   return z.c[0];
> > }
> >
> > GCC -O0 returns 1 from main, as it should. GCC -O2 and Clang (any
> > optimization level, even with -fno-elide-constructors) returns 0.
> 
> Thanks for the testcase.
> 
> > (It looks like Clang gets this "wrong" in two ways: first, NRVO is apprently
> > never correct on a type whose tail padding could be reused
> 
> Hmm, I was thinking that the NRVO was fine, but the caller shouldn't
> elide the copy because the function might clobber tail padding.  But
> that gets back to my initial question, since P0135 requires that
> elision.  Avoiding NRVO here doesn't conflict with P0135, but it does
> create a new ABI requirement that existing code might violate.
> 
> Given John's observation that P0135 can't even work in theory for the case of a base class with virtual bases, it seems like disabling P0135 for the case of initializing a base class of a class with vbases may be the simplest way forward.

We re-use tail padding of all bases, not just virtual bases.  It's true that the Itanium ABI generally initializes things in ascending address order, but there are *two* exceptions.  The first, as you've noted, is virtual bases.  The second is when the primary base class is not the first base class in inheritance order:

    struct A {
      char c;
      A() : c(15) {}
    };

    struct B {
      virtual void foo() {}
      char d;
    };

    struct C : A, B {};

    int main() {
        C c;
    }

Here the 'A' base is allocated in the tail padding of the 'B' base.  Now, 'B' is not technically trivially-copyable, but...

Also, it's a big world, and other/alternative/future ABIs might want to do all sorts of things.  It's also not that hard to imagine future language features that would rely on knowing whether a constructor is initializing a base sub-object or a complete object (for example, the language could provide a way to declare constructors that are only allowed to initialize one or the other).

It seems to me that the maximally correct thing is to disable the P0135 mandate for the case of initializing a base sub-object, full stop.  If we can define conditions in which it's acceptable to elide the copy, great, but that should be up to the implementation / ABI.

(Semantic features like base-only constructors wouldn't prevent us from doing this best-effort today because adding one to an existing type is an ODR violation anyway.  We could easily adjust the ABI rule to disable in-place copy elision into base subobjects when the chosen copy constructor is base-only or something.)

John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20160721/eb0c64ea/attachment-0001.html>


More information about the cxx-abi-dev mailing list