[cfe-dev] [RFC] Attribute that can be used to instruct clang to pass and return non-trivial structs directly

David Blaikie via cfe-dev cfe-dev at lists.llvm.org
Wed Nov 15 08:13:27 PST 2017


On Tue, Nov 14, 2017 at 12:13 PM John McCall via cfe-dev <
cfe-dev at lists.llvm.org> wrote:

> On Nov 14, 2017, at 2:12 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>
> On 13 Nov 2017 23:46, "John McCall via cfe-dev" <cfe-dev at lists.llvm.org>
> wrote:
>
>
> > On Nov 13, 2017, at 8:37 PM, Akira Hatanaka via cfe-dev <
> cfe-dev at lists.llvm.org> wrote:
> >
> > I'd like to propose an attribute that can be used to instruct clang to
> pass and return non-trivial C++ structs directly when it's possible to do
> so. Any feedback would be greatly appreciated.
> >
> > ### Motivation for the attribute
> > We have a user who wants to adopt smart pointers to manage lifetime of
> objects. The current code looks like this:
> >
> > struct Object {
> >   ...
> >   void release();
> > };
> >
> > Object *getObject();
> >
> > void test() {
> >   Object *t = getObject();
> >   ...
> >   // Users have to call 'release' manually.
> >   t->release();
> > }
> >
> > The smart pointer the user plans to use is a thin C++ template wrapper
> whose only data member is the raw pointer that points to the object being
> managed:
> >
> > template<class T>
> > struct SmartPtr {
> >   T *ptr;
> >   SmartPtr(T *p);
> >   SmartPtr(const SmartPtr &);
> >   ~SmartPtr() {
> >     ptr->release();
> >   }
> > };
> >
> > SmartPtr<Object> getObject();
> >
> > void test() {
> >   SmartPtr<Object> t = getObject();
> >   ...
> >   // The object 't' points to is automatically released when ~SmartPtr
> is called.
> > }
> >
> > The problem with the code above is that, since SmartPtr is considered to
> be non-trivial for the purpose of calls according to the C++ ABI, function
> getObject now returns its return value indirectly via an implicit pointer
> parameter passed to the function even though the struct has the same
> in-memory representation as a raw pointer. This breaks ABI compatibility
> for users who use getObject in their code but cannot rewrite their code to
> use the new API.
> >
> > ### The proposed attribute
> > The attribute (tentatively named "trivial_abi") will be used to annotate
> C++ structs and instruct clang to pass and return the struct indirectly
> when it's possible to do so. This means that the presence of a non-trivial
> destructor or copy constructor will not force the struct to be passed or
> returned indirectly, but it doesn't guarantee the struct will always be
> passed or returned directly (the struct have to be passed indirectly if its
> size is too large or there is another data member that is non-trivial, for
> example).
>
> To clarify, the expected behavior here is that the type would get passed
> using the C ABI for the underlying struct type instead of being forced to
> use an indirect ABI.  There isn't a direct guarantee that the ABI will
> match that of a pointer if the struct happens to only contain a pointer.
> Our internal client is satisfied with that.
>
> I guess this attribute would be illegal on a class with virtual methods or
> virtual bases?  That's probably simplest for now.
>
> In semantic terms, this attribute is a guarantee that the type can be
> destructively moved with memcpy, combined with an assertion that there is
> no code in the program that isn't aware of that (and therefore that it's
> acceptable to rely on that in the ABI).  By a "destructively move" I mean,
> effectively, a move-initialization immediately followed by the destruction
> of the source.
>
>
> Even when not initializing a function parameter / return object, I assume?
> (The attribute name seems a little too specific for such cases, but it's
> probably fine for it to describe only the primary intended use.)
>
>
> Yes, I think it would enable such an optimization.  Of course, if we were
> pursuing that optimization in general, I think we'd want to also add a
> non-ABI-breaking attribute that could be applied to basically everything in
> the STL.
>
> I think you also need to extend this rule to cover the attributed types:
> http://eel.is/c++draft/special#class.temporary-3
>
>
> Agreed.  We may need to insert an implicit copy of the value to perform
> the call, and since that can be detectable (by ill-advised code), we need a
> call-out that says that's legal.
>
> I don't think we'd thought about documenting this (additionally) in terms
> of precise edits to the standard, but that's not a bad idea.
>

Wasn't there a Clang policy about language extensions that required they be
at least proposed for standardization? (I can't seem to find that anymore,
but I think Doug proposed it/wrote it up at some point)

Maybe attributes don't fall under this policy? Not sure.


>
> Would an aggregate containing only such types and trivial types inherit
> the same property? (More accurately, a type whose copy or move ctor and
> dtor would be trivial if not for calling corresponding members on such a
> type.)
>
>
> Yes.  I would phrase it the other way: being required to be passed
> indirectly is a property that's inherited by containing aggregates.
>
> John.
>
> Or is that a bridge too far for what you're trying to achieve? Once we
> implement this, will it be considered part of our immutable ABI surface, or
> will we have time to iterate on such design questions?
>
>
> The destruction convention for arguments of trivial_abi types would be
> that the callee is responsible for destroying the object (regardless of how
> it is passed under the C ABI).  This allows the callee to just initialize
> the parameter variable with the passed representation and then destroy that
> on exit.  If the caller were responsible for destroying the object, we
> would need to implicitly copy in order to protect against the callee
> changing the value of its parameter variable before returning.  Destroying
> a parameter at callee exit is not the Itanium rule, but it is the MSVC
> rule, and it is permitted by [expr.call]p4.  We will arguably be required
> to violate the rule that the parameter's destruction "occurs within the
> context of the calling function", but I believe we can implement this to at
> least not violate the rule that the parameter is destroyed outside of the
> scope of a function-try-block in the callee.
>
>
> Seems very reasonable to me. I'd love for libc++ to start using this for
> unique_ptr in its unstable ABI mode.
>
> John.
>
> >
> > Besides avoiding ABI breakage, the attribute can potentially improve
> performance since the extra load instruction to get the value of the
> pointer isn't needed when the struct is passed or returned directly.
> > _______________________________________________
> > cfe-dev mailing list
> > cfe-dev at lists.llvm.org
> > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
>
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20171115/95bdfb3b/attachment.html>


More information about the cfe-dev mailing list