As has been discussed in various github PRs recently, I'd like to change libffi's behaviour regarding large struct arguments. When passing a struct by value, most (all?) ABI definitions ask that you try to fit structs up to a certain size into registers, and if they are too large, make a copy and pass them on the stack. Libffi's current behaviour is to fit small structs in registers, but then if something is too large, pass it by reference, leaving it as an exercise for the user to make their own copies. Many libffi users, like cpython, do this special work themselves. I don't like this because it exposes this ABI detail, the threshold for struct sizes, to the libffi caller. Libffi should be making this copy itself. The struct_by_value_big.c test checks for this, and most ports fail today Changing this behaviour won't introduce regressions for libffi users, and eventually they will be able to remove their special handling of large struct args. AG
On May 28, 2022 09:40:34 Anthony Green <green@moxielogic.com> wrote:
> As has been discussed in various github PRs recently, I'd like to
> change libffi's behaviour regarding large struct arguments.
>
> When passing a struct by value, most (all?) ABI definitions ask that
> you try to fit structs up to a certain size into registers, and if
> they are too large, make a copy and pass them on the stack.
> Libffi's current behaviour is to fit small structs in registers, but
> then if something is too large, pass it by reference, leaving it as an
> exercise for the user to make their own copies. Many libffi users,
> like cpython, do this special work themselves. I don't like this
> because it exposes this ABI detail, the threshold for struct sizes, to
> the libffi caller. Libffi should be making this copy itself.
>
> The struct_by_value_big.c test checks for this, and most ports fail
> today Changing this behaviour won't introduce regressions for libffi
> users, and eventually they will be able to remove their special
> handling of large struct args.
>
> AG
How might this change interact with, say, C++ types tagged with
clang::trivial_abi? Not all types are trivially memcpy-moveable, sadly.
On Sat, May 28, 2022 at 11:38 AM <dancol@dancol.org> wrote:
> How might this change interact with, say, C++ types tagged with clang::trivial_abi? Not all types are trivially memcpy-moveable, sadly.
Mapping C++ semantics to the C ABI has always been an exercise for the
libffi user.
AG
Interestingly, the powerpc-unknown-eabisim port mostly got this right,
except that it never passed small structs in registers as it should.
Does anybody care about the 32-bit embedded Power port anymore?
AG
On Sat, May 28, 2022 at 9:40 AM Anthony Green <green@moxielogic.com> wrote:
>
> As has been discussed in various github PRs recently, I'd like to
> change libffi's behaviour regarding large struct arguments.
>
> When passing a struct by value, most (all?) ABI definitions ask that
> you try to fit structs up to a certain size into registers, and if
> they are too large, make a copy and pass them on the stack.
> Libffi's current behaviour is to fit small structs in registers, but
> then if something is too large, pass it by reference, leaving it as an
> exercise for the user to make their own copies. Many libffi users,
> like cpython, do this special work themselves. I don't like this
> because it exposes this ABI detail, the threshold for struct sizes, to
> the libffi caller. Libffi should be making this copy itself.
>
> The struct_by_value_big.c test checks for this, and most ports fail
> today Changing this behaviour won't introduce regressions for libffi
> users, and eventually they will be able to remove their special
> handling of large struct args.
>
> AG
While this is technically an ABI change, if the "old" ABI never worked, I can't see how this would break anything by changing. I will add that passing structs is likely more complex than you think ;-)
On May 31, 2022 11:53:56 DJ Delorie via Libffi-discuss
<libffi-discuss@sourceware.org> wrote:
> While this is technically an ABI change, if the "old" ABI never worked,
> I can't see how this would break anything by changing.
>
> I will add that passing structs is likely more complex than you think ;-)
The problem is that whether the old API was broken or not, changing it can
break working code. What do the libffi people think about using symbol
versioning?
On 2022-05-31 09:47, dancol@dancol.org wrote:
> On May 31, 2022 11:53:56 DJ Delorie via Libffi-discuss <libffi-discuss@sourceware.org> wrote:
>
>> While this is technically an ABI change, if the "old" ABI never worked,
>> I can't see how this would break anything by changing.
>>
>> I will add that passing structs is likely more complex than you think ;-)
>
> The problem is that whether the old API was broken or not, changing it can break working code. What do the libffi people think about using symbol versioning?
ELF symbol versioning keeps old binaries working.
Newly recompiled programs will break in cases when recompilation
alone doesn't fix the issue.
Versioning works really well for things like new members being added to
a structure, where newly recompiled code picks up the new declaration
and so recompiling == fixing.
It won't work in a situation where some FFI-using code has worked around
for some libffi behavior and expects that not to be moving target;
then someone has to fix the code. People recompile code all the time without
fixing anything in it.
On Tue, May 31, 2022 at 11:53 AM DJ Delorie <dj@redhat.com> wrote: > While this is technically an ABI change, if the "old" ABI never worked, > I can't see how this would break anything by changing. I wouldn't even call this an ABI change. The new implementation will be ABI compatible. This is really a bug fix. In fact, some ports have always done the right thing. > I will add that passing structs is likely more complex than you think ;-) The rules about passing small structs in registers can be complex, but libffi always did that right. AFAICT, the only complexity around passing large structs in memory has to do with structure alignment, but we have all of the info needed to do that right. I really think this is worth fixing, even after all of these years. Users currently need to know the threshold size for structures to pass in memory vs registers, and this varies per target, and per ABI. It's an abstraction leak that we can easily fix without causing too much pain (if any). AG
Anthony Green <green@moxielogic.com> writes:
>> While this is technically an ABI change, if the "old" ABI never worked,
>> I can't see how this would break anything by changing.
>
> I wouldn't even call this an ABI change. The new implementation will
> be ABI compatible. This is really a bug fix. In fact, some ports
> have always done the right thing.
Right, I was using ABI to mean "the ABI of the target we're composing a
foreign call for", not the call ABI of the libffi library.
If we could not correctly call functions that took large structures, and
now we can, we have changed the target ABI but shouldn't break any
existing software that worked correctly before.
I agree it's worth fixing, we just must be careful we actually *don't*
break any currently-working program.