llvm / llvm/llvm-project

[X86] LLVM -Oz generates 8-byte move for small constants

Open
#166,126 2 comments 0 reactions 0 assignees View on GitHub
backend:X86 missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

In certain cases, Clang -Oz generates a full 8 byte move for loading single byte constants. This does not seem to affect -Os or any other levels.

Test case (https://godbolt.org/z/G3EfdKxMs):
```
#include
#include
#include

size_t test(uint64_t p[6], size_t index) {
uint64_t p2[6];
memcpy(p2, p, sizeof(p2));

for (size_t i = 0; i < 6; i++) {
if ((p2[i] & (1ULL << index)) != 0) {
return i;
}
}

return 6;
}
```
This generates with Clang 21.1.0
```
test:
mov rcx, rsi
movups xmm0, xmmword ptr [rdi]
movups xmm1, xmmword ptr [rdi + 16]
movups xmm2, xmmword ptr [rdi + 32]
movaps xmmword ptr [rsp - 24], xmm2
movaps xmmword ptr [rsp - 40], xmm1
movaps xmmword ptr [rsp - 56], xmm0
movabs rdx, 1
shl rdx, cl
xor eax, eax
.LBB0_1:
cmp rax, 6
je .LBB0_4
test qword ptr [rsp + 8*rax - 56], rdx
jne .LBB0_4
inc rax
jmp .LBB0_1
.LBB0_4:
ret
```
where, `movabs rdx, 1` is used instead of `mov edx, 1` or `push 1/pop rdx`. A similar reproducer can be made in Zig as well. The issue does not appear if the `memcpy` is removed.

Additionally in this case, there are other missed optimisations as the `memcpy` can be eliminated (which happens with -O2), and `i` can be demoted to 32 bits.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.