llvm / llvm/llvm-project

[X86][AVX512] missed fold scalar load > embed broadcast

Open
#223,133 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

https://godbolt.org/z/4hb7vfz5K

```c++
void foo(int32_t &a , int32_t &b, int32_t &c){
c = a > b ? a : b;
}

void foo2(uint32_t &a , uint32_t &b, uint32_t &c){
c = a < b ? a : b;
}

float foo3(int &a){
return a;
}
```
```asm
foo(int&, int&, int&):
vmovd xmm0, dword ptr [rsi]
vmovd xmm1, dword ptr [rdi]
vpmaxsd xmm0, xmm1, xmm0
vmovd dword ptr [rdx], xmm0
ret

foo2(unsigned int&, unsigned int&, unsigned int&):
vmovd xmm0, dword ptr [rsi]
vmovd xmm1, dword ptr [rdi]
vpminud xmm0, xmm1, xmm0
vmovd dword ptr [rdx], xmm0
ret

foo3(int&):
vcvtsi2ss xmm0, xmm15, dword ptr [rdi]
ret
```
should be
```c++
typedef int32_t i32 [[gnu::vector_size(16)]];
typedef uint32_t u32 [[gnu::vector_size(16)]];
typedef float f32 [[gnu::vector_size(16)]];

void bar(int32_t &a , int32_t &b, int32_t &c){
i32 x = i32{a,0,0,0};
i32 y = i32{b,b,b,b};
i32 z = x > y ? x : y;
c=z[0];
}

void bar2(uint32_t &a , uint32_t &b, uint32_t &c){
u32 x = u32{a,0,0,0};
u32 y = u32{b,b,b,b};
u32 z = x < y ? x : y;
c=z[0];
}

f32 bar3(int32_t &a){
return __builtin_convertvector(i32{a,a,a,a}, f32);
}
```
```asm
bar(int&, int&, int&):
vmovd xmm0, dword ptr [rdi]
vpmaxsd xmm0, xmm0, dword ptr [rsi]{1to4}
vmovd dword ptr [rdx], xmm0
ret

bar2(unsigned int&, unsigned int&, unsigned int&):
vmovd xmm0, dword ptr [rdi]
vpminud xmm0, xmm0, dword ptr [rsi]{1to4}
vmovd dword ptr [rdx], xmm0
ret

bar3(int&):
vcvtdq2ps xmm0, dword ptr [rdi]{1to4}
ret
```

In the first case (min/max/abs), this can save one load instruction.
In the second case (int2float), this can avoid the unnecessary second source operand.

Contributor guide

Open the contributing guide

Research direction

Start with the Godbolt reproducer and compare the generated AVX-512 assembly for foo/bar, foo2/bar2, and foo3/bar3. Investigate the compiler's x86 vector optimization path for scalar-load folding and embedded broadcasts. Done means the first cases use a scalar load with an embedded broadcast, while the integer-to-float case avoids the unnecessary second source operand.

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
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.