llvm / llvm/llvm-project

[AArch64] Suboptimal code for testing if all elements of a NEON vector are equal

Open
#185,224 3 comments 0 reactions 0 assignees View on GitHub
backend:AArch64 missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

# C code
https://godbolt.org/z/Wa61E4dTf
```c++
#include

bool src1(uint32x4_t v) {
bool ret = true;
for (int i = 0; i < 4; i++) ret &= (v[i] == v[0]);
return ret;
}

bool tgt1(uint32x4_t v) {
auto max = vmaxvq_u32(v);
auto min = vminvq_u32(v);
return max == min;
}
```

# Generated assembly
```asm
src1:
dup v1.4s, v0.s[0]
mov w8, #1
cmeq v0.4s, v0.4s, v1.4s
mvn v0.16b, v0.16b
umaxv s0, v0.4s
fmov w9, s0
bic w0, w8, w9
ret

tgt1:
umaxv s1, v0.4s
uminv s0, v0.4s
fmov w8, s1
fmov w9, s0
cmp w8, w9
cset w0, eq
ret
```

# MCA timings
```asm
Iterations: 100
Instructions: 800
Total Cycles: 1602
Total uOps: 800

Dispatch Width: 3
uOps Per Cycle: 0.50
IPC: 0.50
Block RThroughput: 2.7

Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1] [2] [3] [4] [5] [6] Instructions:
1 3 0.50 dup v1.4s, v0.s[0]
1 1 0.33 mov w8, #1
1 3 0.50 cmeq v0.4s, v0.4s, v1.4s
1 3 0.50 mvn v0.16b, v0.16b
1 4 0.50 umaxv s0, v0.4s
1 3 0.50 fmov w9, s0
1 1 0.33 bic w0, w8, w9
1 1 1.00 U ret
```

```asm
Iterations: 100
Instructions: 700
Total Cycles: 901
Total uOps: 700

Dispatch Width: 3
uOps Per Cycle: 0.78
IPC: 0.78
Block RThroughput: 2.3

Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1] [2] [3] [4] [5] [6] Instructions:
1 4 0.50 umaxv s1, v0.4s
1 4 0.50 uminv s0, v0.4s
1 3 0.50 fmov w8, s1
1 3 0.50 fmov w9, s0
1 1 0.33 cmp w8, w9
1 1 0.33 cset w0, eq
1 1 1.00 U ret
```

# Alive proof
https://alive2.llvm.org/ce/z/Q9ghYB
```llvm-ir

----------------------------------------
define i1 @src1(<4 x i32> noundef %v) noundef {
entry:
%#0 = shufflevector <4 x i32> noundef %v, <4 x i32> poison, 0, 0, 0, 0
%#1 = icmp ne <4 x i32> noundef %v, %#0
%#2 = bitcast <4 x i1> %#1 to i4
%#3 = icmp eq i4 %#2, 0
ret i1 %#3
}
=>
define i1 @tgt1(<4 x i32> noundef %v) noundef {
entry:
%vmaxvq_u32.i = reduce_umax <4 x i32> noundef %v
%vminvq_u32.i = reduce_umin <4 x i32> noundef %v
%cmp = icmp eq i32 %vmaxvq_u32.i, %vminvq_u32.i
ret i1 %cmp
}
Transformation seems to be correct!
```

Contributor guide

Open the contributing guide

Research direction

Start with the C++ reproducer, Godbolt assembly, MCA timings, and Alive2 proof linked in the issue. Investigate LLVM's AArch64 NEON code-generation path for the all-elements-equal pattern and compare it with the vmaxvq/uminvq sequence shown. Done means the equivalent comparison is recognized and produces better code, with correctness preserved.

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
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.