[PATCH] D51502: [X86] Fix register resizings for inline assembly register operands.

Nick Desaulniers via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 13 09:58:59 PDT 2018


nickdesaulniers added a comment.

The behavior of this version of this patch is not quite right just yet, in terms of matching GCC's curious behavior.

Consider my test case from the above comment again:

  // cc -O3 -m32 -c -o edx.o
  long long foo () {
      register long long x asm("edx");
      asm("call bar": "=r"(x));
      return x;
  }
  
  long long bar () {
      return 0x0011223344556677;
  }

gcc produces the following disassembly:

  ; objdump -Dr edx.o
  00000000 <foo>:
     0:   e8 fc ff ff ff          call   1 <foo+0x1>
                          1: R_386_PC32   bar
     5:   89 d0                   mov    %edx,%eax
     7:   89 ca                   mov    %ecx,%edx
     9:   c3                      ret

while Clang plus this version of this patch produces:

  00000000 <foo>:
     0:   53                      push   %ebx
     1:   56                      push   %esi
     2:   e8 09 00 00 00          call   10 <bar>
     7:   89 d8                   mov    %ebx,%eax
     9:   89 f2                   mov    %esi,%edx
     b:   5e                      pop    %esi
     c:   5b                      pop    %ebx
     d:   c3                      ret

It seems for 32b x86, when returning 64b, the lower 32b is returned in `%eax` and the upper 32b in `%edx`, which both compilers get correct here.  What differs is the source registers of the output argument to the inline asm block.

To see why this is critical, the crashing test case for the bugreport in 36378 <https://bugs.llvm.org/show_bug.cgi?id=36378> is from the Linux kernel's `__get_user_8` function, which is defined in arch/x86/lib/getuser.S <https://elixir.bootlin.com/linux/v4.19-rc3/source/arch/x86/lib/getuser.S#L22>.  Specifically the comment:

> Outputs: ...
>  %[r|e]dx contains zero-extended value
>  %ecx contains the high half for 32-bit __get_user_8

So in order to support this unspecified calling convention, it's critical that we //read// from `%ecx` for the upper 32b, and `%edx` for the lower 32b following the `call`.  From GCC's disassembly, you can see this in the source of the `mov`s (left operand) after the `call`.


Repository:
  rL LLVM

https://reviews.llvm.org/D51502





More information about the llvm-commits mailing list