I was intrigued by clang's ability to convert many == comparisons of small integers to to one big SIMD instruction, but then I noticed something strange. Clang generated "worse" code(in my amateur evaluation) when I had 7 comparisons compared to the code when I had 8 comparisons.
bool f1(short x){
return (x==-1) | (x == 150) |
(x==5) | (x==64) |
(x==15) | (x==223) |
(x==42) | (x==47);
}
bool f2(short x){
return (x==-1) | (x == 150) |
(x==5) | (x==64) |
(x==15) | (x==223) |
(x==42);
}
My question is this a small performance bug, or clang has a very good reason for not wanting to introduce a dummy comparison(i.e. pretend that there is one extra comparison with one of the 7 values) and use one more constant in the code to achieve it.
godbolt link here:
# clang(trunk) -O2 -march=haswell
f1(short):
vmovd xmm0, edi
vpbroadcastw xmm0, xmm0 # set1(x)
vpcmpeqw xmm0, xmm0, xmmword ptr [rip + .LCPI0_0] # 16 bytes = 8 shorts
vpacksswb xmm0, xmm0, xmm0
vpmovmskb eax, xmm0
test al, al
setne al # booleanize the parallel-compare bitmask
ret
vs.
f2(short):
cmp di, -1
sete r8b
cmp edi, 150
sete dl
cmp di, 5 # scalar checks of 3 conditions
vmovd xmm0, edi
vpbroadcastw xmm0, xmm0
vpcmpeqw xmm0, xmm0, xmmword ptr [rip + .LCPI1_0] # low 8 bytes = 4 shorts
sete al
vpmovsxwd xmm0, xmm0
vmovmskps esi, xmm0
test sil, sil
setne cl # SIMD check of the other 4
or al, r8b
or al, dl
or al, cl # and combine.
ret
quickbench does not seem to work because IDK how to provide -mavx2 flag to it. (Editor's note: simply counting uops for front-end cost shows this is obviously worse for throughput. And also latency.)