llvm / llvm/llvm-project

[X86] Wrong code for inline assembly with memory operands with segment registers

Open
#187,893 4 comments 0 reactions 0 assignees View on GitHub
backend:X86 inline-asm miscompilation
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

```c
int foo(int __seg_gs *p) {
int v;
asm ("movl %1, %0" : "=r"(v) : "m"(*p));
return v - *p;
}
```
Compiling with GCC produces
```asm
foo:
movl %gs:(%rdi), %eax
subl %gs:(%rdi), %eax
ret
```
Compiling with Clang produces
```asm
foo:
movl (%rdi), %eax
subl %gs:(%rdi), %eax
retq
```
Note how the segment override is missing in the instruction coming from the inline assembly.

https://godbolt.org/z/e4bG3Mc5h

This is one of the issues affecting glibc for i386 getting built with Clang: see [`sysdeps/i386/nptl/tls.h`](https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/i386/nptl/tls.h;h=87dcf5dc1f47462e63eb14b089c7b9792bd1f577;hb=HEAD#l255)'s definition of `THREAD_GSCOPE_RESET_FLAG()`.

This looks to be handled correctly in Clang, the generated LLVM IR still contains the address space (the `addrspace(256)` in `tail call i32 asm "movl $1, $0", "=r,*m,~{dirflag},~{fpsr},~{flags}"(ptr addrspace(256) elementtype(i32) %0)`), but the backend does not translate this in a way that preserves that.

Contributor guide

Open the contributing guide

Research direction

Start with the provided C reproducer and compare the LLVM IR, especially the addrspace(256) inline-assembly operand, with the generated x86 assembly. Trace the backend lowering that handles the memory operand; done means the inline-assembly load preserves the segment-register override, matching the non-inline-assembly access.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.