Assigned register collision when inline assembly compiled with clang

Viewed 120

Consider the following sample program (targeting Linux/x86-64):

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
  unsigned arg1 = strtoul(argv[1], NULL, 0);
  unsigned arg2 = strtoul(argv[2], NULL, 0);
  asm(
    "mov %[arg1], %%ecx\n\t"
    "add %[arg2], %[arg1]\n\t"
    "add %[arg2], %%ecx\n\t"
    "xchg %%ecx, %[arg2]"
    : [arg1] "+&abdSD" (arg1), [arg2] "+&abdSD" (arg2)
    :
    : "cc", "ecx");
  printf("%u %u\n", arg1, arg2);
}

(xchg is used just for easy grepping of compiled instructions in listing.)

With GCC, it works as expected - different registers are assigned to arg1 and arg2, for example:

    11bf:       e8 cc fe ff ff          callq  1090 <strtoul@plt>
    11c4:       89 da                   mov    %ebx,%edx
    11c6:       89 d1                   mov    %edx,%ecx
    11c8:       01 c2                   add    %eax,%edx
    11ca:       01 c1                   add    %eax,%ecx
    11cc:       91                      xchg   %eax,%ecx

(so, arg1 in edx, arg2 in eax)

But, compiling with Clang (confirmed on 6.0 and 10.0) results in assigning the same register for arg1 and arg2:

  401174:       e8 d7 fe ff ff          callq  401050 <strtoul@plt>
  401179:       44 89 f0                mov    %r14d,%eax ; <--
  40117c:       89 c1                   mov    %eax,%ecx
  40117e:       01 c0                   add    %eax,%eax ; <-- so, arg1 and arg2 both in eax
  401180:       01 c1                   add    %eax,%ecx
  401182:       91                      xchg   %eax,%ecx

The issue remains with multiple variations as e.g.: + instead of +& in constraint strings; numeric forms like %0 to address operands; replacing xchg with another rare instruction; and so on.

I have been expecting, from the basic principles, that compilerʼs logic to assign output locations will always assign different locations to different output operands, whatever constraints are defined for them; and the same works among the input operands set. (Modifiers like '+', '&' add more rules to placement logic but canʼt erode the main principles.)

Is there a some trivial aspect Iʼve overlooked?

UPD: reported to LLVM.

0 Answers
Related