https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82261
--- Comment #5 from Paweł Bylica <chfast at gmail dot com> ---
The form that is well defined for a shift count of 0 is not turned into
SHLD/SHRD either. SHLD/SHRD with a count of 0 leave the destination unchanged,
which is the s == 0 result here:
unsigned long fshl(unsigned long hi, unsigned long lo, unsigned s)
{
return s == 0 ? hi : (hi << s) | (lo >> (64 - s));
}
unsigned long fshr(unsigned long hi, unsigned long lo, unsigned s)
{
return s == 0 ? lo : (lo >> s) | (hi << (64 - s));
}
GCC 16.2 and trunk -O2 (fshr is the same with the shifts swapped):
fshl:
movl %edx, %ecx
movq %rdi, %rax
negl %ecx
shrq %cl, %rsi
movl %edx, %ecx
salq %cl, %rax
orq %rax, %rsi
testl %edx, %edx
movq %rdi, %rax
cmovne %rsi, %rax
ret
GCC 15.2 used SHLD/SHRD, but kept the branch on s == 0:
fshl:
movq %rdi, %rax
testl %edx, %edx
je .L2
movl %edx, %ecx
shldq %cl, %rsi, %rax
.L2:
ret
Clang:
fshl:
movl %edx, %ecx
movq %rdi, %rax
shldq %cl, %rsi, %rax
retq
intx (a multi-precision integer library) writes its funnel shifts this way.