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

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Tue Nov 14 11:12:14 PST 2017


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.)

I think you also need to extend this rule to cover the attributed types:
http://eel.is/c++draft/special#class.temporary-3

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.)
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20171114/4f7bf4fa/attachment.html>


More information about the cfe-dev mailing list