https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82693

            Bug ID: 82693
           Summary: gcc/clang calling convension mismatch
           Product: gcc
           Version: 7.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: vanyacpp at gmail dot com
  Target Milestone: ---

Recently I have discovered that empty arguments are passed differently by gcc
and by clang.

struct empty
{};

void f(empty, empty, empty);

int main()
{
    f(empty{}, empty{}, empty{});
}

GCC pushed 0 to stack for each empty struct while clang does nothing.

main(GCC):
  sub rsp, 16
  xor eax, eax
  push rax
  push rax
  push rax
  call f(empty, empty, empty)
  xor eax, eax
  add rsp, 40
  ret

main(clang):
  push rax
  call f(empty, empty, empty)
  xor eax, eax
  pop rcx
  ret

I would say that I like the code generated by clang more. As the struct is
empty no value needs to be passed. In a case the callee needs to take the
address of the argument it can allocate the argument in its own frame as the
argument is passed by value.

As the code generated differs I wondered if I can construct an example where
parameters get wrong values because of this. Turned out it wasn't very
difficult.

The following program

struct empty
{};

struct big
{
    int a;
    int b;
    int c;
    int d;
    int e;
};

void print_7th(empty, empty, empty, empty, empty, empty, big seventh)
{
    std::cout << seventh.a
       << ' ' << seventh.b
       << ' ' << seventh.c
       << ' ' << seventh.d
       << ' ' << seventh.e << std::endl;
}

int main()
{
    empty e;
    print_7th(e, e, e, e, e, e, big{1, 2, 3, 4, 5});
}

prints different output when print_7th and main are compiled with different
compilers:

./gcc-to-gcc
1 2 3 4 5
./clang-to-gcc
5 32767 0 0 635911936
./gcc-to-clang
192 0 -1204279016 32765 1
./clang-to-clang
1 2 3 4 5

Another easy way to see that compilers pass arguments differently is compiling
this code:

int sum(empty, empty, empty, empty, empty, empty, big seventh)
{
    return seventh.a + seventh.b + seventh.c + seventh.d + seventh.e;
}

GCC generates:

sum:
  mov eax, DWORD PTR [rsp+60]
  add eax, DWORD PTR [rsp+56]
  add eax, DWORD PTR [rsp+64]
  add eax, DWORD PTR [rsp+68]
  add eax, DWORD PTR [rsp+72]
  ret

while clang generates:

sum:
  mov eax, dword ptr [rsp + 12]
  add eax, dword ptr [rsp + 8]
  add eax, dword ptr [rsp + 16]
  add eax, dword ptr [rsp + 20]
  add eax, dword ptr [rsp + 24]
  ret

I don't know which compiler is right in this case and which conform to SysV ABI
better. I would prefer GCC to adopt clang behavior as it is more efficient.
Because if we have two popular major compilers that both are widely used and
are incompatible with each other and we have to break compatibility of one of
them it's better to choose the better alternative. Also using empty struct as
parameters is common in C++ code, and it's great to make them free.

Reply via email to