[llvm-bugs] [Bug 35046] New: gcc/clang calling convension mismatch

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Oct 23 16:29:19 PDT 2017


https://bugs.llvm.org/show_bug.cgi?id=35046

            Bug ID: 35046
           Summary: gcc/clang calling convension mismatch
           Product: clang
           Version: unspecified
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: -New Bugs
          Assignee: unassignedclangbugs at nondot.org
          Reporter: vanyacpp at gmail.com
                CC: llvm-bugs at lists.llvm.org

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.

I have already reported this issue against gcc:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82693 I report this issue here
too, to let clang developers know about the issue too.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20171023/34096a1b/attachment.html>


More information about the llvm-bugs mailing list