llvm / llvm/llvm-project

[X86] Redundant spill/reloads when indexing ext_vector_type with non-constant

Open
#190,725 1 comment 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

This appears to happen only in specific circumstances when the vector comes from memory (indexing vectors from a large struct appears to be fine, minus the redundant index masking). The spill is never hoisted from the loop in the from-register case, but the pointer-cast workaround has been reliable.

Godbolt: https://godbolt.org/z/veTnvaqGP

```cpp
using v_int = int [[clang::ext_vector_type(16)]];

int ProcessItems(v_int a, int mask) {
int s = 0;

for (; mask != 0; mask &= mask - 1) {
int i = __builtin_ctz(mask);
s += a[i];
}
return s;
}

int ProcessItems_B(const v_int& a, int mask) {
int s = 0;

for (; mask != 0; mask &= mask - 1) {
int i = __builtin_ctz(mask);
s += a[i];
}
return s;
}
// Workaround
int ProcessItems_C(const v_int& a, int mask) {
int s = 0;

for (; mask != 0; mask &= mask - 1) {
int i = __builtin_ctz(mask);
s += ((int*)&a)[i];
}
return s;
}
```

```asm
ProcessItems_B(int vector[16] const&, int):
test esi, esi
je .LBB1_1
push rbp
mov rbp, rsp
and rsp, -32
sub rsp, 96
vmovaps ymm0, ymmword ptr [rdi]
vmovaps ymm1, ymmword ptr [rdi + 32]
xor eax, eax
.LBB1_4:
tzcnt ecx, esi
vmovaps ymmword ptr [rsp], ymm0
vmovaps ymmword ptr [rsp + 32], ymm1
and ecx, 15
add eax, dword ptr [rsp + 4*rcx]
blsr esi, esi
jne .LBB1_4
mov rsp, rbp
pop rbp
vzeroupper
ret
.LBB1_1:
xor eax, eax
ret
```

```asm
ProcessItems_C(int vector[16] const&, int):
xor eax, eax
test esi, esi
je .LBB2_2
.LBB2_1:
tzcnt ecx, esi
add eax, dword ptr [rdi + 4*rcx]
blsr esi, esi
jne .LBB2_1
.LBB2_2:
ret
```

Contributor guide

Open the contributing guide

Research direction

Start with the Godbolt reproducer and compare ProcessItems_B with the pointer-cast workaround in ProcessItems_C. Investigate the compiler path for non-constant indexing of ext_vector_type values passed by reference, then verify generated x86 code. Done means the reference case no longer emits redundant per-iteration vector spills and reloads while preserving the loop's result.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.