[X86][GVN] Redundant pointer load (movq) not eliminated across conditional branch
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
**Clang:** Ubuntu clang version 22.1.8 · reproduced on Godbolt 22.1.0
**Target:** x86_64-pc-linux-gnu · **Compile:** `clang -O3`
**Labels:** `missed-optimization` `backend:X86`
## Summary
With `-O3`, Clang loads the global pointer `e` into `%rcx` on the first branch path
(`if (c)`), then **reloads the same pointer** into `%rax` on a subsequent branch
(`if (ag)`), even though no store to `e` occurs between the two loads.
`GVN` should recognize `movq e(%rip), %rax` as redundant and replace it with
`movq %rcx, %rax` (register copy).
## Reproducer
```c
int ag, b;
short c;
long d;
int *e;
void f(void) {
if (c)
*e = d;
if (ag)
b = *e;
}
```
## Actual output (`-O3`)
```asm
f:
cmpw $0, c(%rip)
je .LBB0_2
movl d(%rip), %eax
movq e(%rip), %rcx
movl %eax, (%rcx)
cmpl $0, ag(%rip)
je .LBB0_4
movq e(%rip), %rax
movl (%rax), %eax
movl %eax, b(%rip)
retq
c:
d:
e:
ag:
b:
```
**Bug:**
```asm
movq e(%rip), %rcx ; load pointer e → %rcx (first path)
movl %eax, (%rcx)
cmpl $0, ag(%rip)
je .LBB0_4
movq e(%rip), %rax ; REDUNDANT: reload e → %rax (already in %rcx)
movl (%rax), %eax
```
Between `movq e(%rip), %rcx` and `movq e(%rip), %rax` there is no store to `e`.
The second load should be `movq %rcx, %rax`.
## Expected output
```asm
movq e(%rip), %rcx ; load e once
movl %eax, (%rcx)
cmpl $0, ag(%rip)
je .done
movl (%rcx), %eax ; use %rcx directly — no memory reload
movl %eax, b(%rip)
.done:
retq
```
## `-O0` for comparison
```asm
f:
pushq %rbp
movq %rsp, %rbp
cmpw $0, c(%rip)
je .LBB0_2
movq d(%rip), %rax
movl %eax, %ecx
movq e(%rip), %rax
movl %ecx, (%rax)
cmpl $0, ag(%rip)
je .LBB0_4
movq e(%rip), %rax
movl (%rax), %eax
movl %eax, b(%rip)
popq %rbp
retq
c:
d:
e:
ag:
b:
```
## Godbolt
https://godbolt.org/z/x37Tfha98
## Root cause
`GVN` tracks available expressions within and across basic blocks. Here, `e` is
loaded in block LBB0_1 and its value enters `%rcx`. Block LBB0_3 (the `if (ag)`
branch) re-loads `e` instead of using the value that arrived via the dominating
load in LBB0_1. No call or store to `e` exists on the path from LBB0_1 to LBB0_3,
so GVN should be able to forward `%rcx` without a second memory access.
Contributor guide
Research direction
Start with the C reproducer and run it through Clang with -O3 for the x86_64 target, comparing the generated assembly with the Godbolt example. Investigate GVN's handling of the pointer load across the conditional branches. Done means the redundant second load of e is eliminated and the generated code uses the existing pointer value without changing behavior.
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
- Mostly clear
- Newbie friendliness
- 45/100