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

John McCall via cfe-dev cfe-dev at lists.llvm.org
Mon Nov 13 23:46:15 PST 2017


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

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.

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




More information about the cfe-dev mailing list