1. 16
  1. 4

    There are some truly weird corner cases with various ABIs. My favourite is the Linux x86-32 ABI, which says that unions are passed as structs and (unlike the *BSD / macOS version of the same ABI) does not use the small structure optimisation. Consider this contrived example function:

    union X
            void *a;
            long b;
    union X ret()
            return (union X){12};

    On FreeBSD, this compiles to:

            movl    $12, %eax

    But on Linux, it’s:

            movl    4(%esp), %eax
            movl    $12, (%eax)
            retl    $4

    On FreeBSD, quite sensibly, a word-sized structure is returned in a word-sized register. On Linux, the caller allocates a word-sized place on the stack and then passes a pointer to it into the callee. The callee then stores the value in the pointer.

    I ended up rewriting a bunch of code to cast things via uintptr_t (which, in spite of effectively being a union of an integer and a pointer in the C spec, is not treated as one in the ABI) to avoid overhead on Linux. Fortunately, the 64-bit ABI fixed this particular issue.

    1. 1

      This is an insightful article that provides useful information also for people that already have some knowledge of Assembly and C++.