[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