Summary
For sdiv i32 %n, C where the signed Hacker’s Delight magic number M is
positive (M < 2^31), x86_64 codegen extracts the sign correction from the
64-bit product with:
movq %rax, %rcx
shrq $63, %rcxThat works, but it is redundant. Because M > 0, sign(n * M) == sign(n), so
the sign bit can be taken from the original input register before the multiply,
saving one instruction.
This is a backend/codegen quality issue, not a correctness issue.
Reproducer
define i32 @sdiv3(i32 %n) {
%r = sdiv i32 %n, 3
ret i32 %r
}Run:
~/llvm-project/build/bin/llc -O2 -mtriple=x86_64-unknown-linux-gnuCurrent output
sdiv3:
movslq %edi, %rax
imulq $1431655766, %rax, %rax
movq %rax, %rcx
shrq $63, %rcx
shrq $32, %rax
addl %ecx, %eax
retqBetter output
sdiv3:
movslq %edi, %rax
shrl $31, %edi
imulq $1431655766, %rax, %rax
shrq $32, %rax
addl %edi, %eax
retqWhy this is valid
When M is positive, the 64-bit product cannot overflow its sign bit in a way
that changes sign independently from the multiplicand:
nis a sign-extendedi32M < 2^31- therefore
|n * M| < 2^62 - so
sign(n * M) == sign(n)
That means the correction term currently computed as (n * M) >> 63 is equal
to n >> 31 for these divisors.
Affected divisors
Any divisor whose signed magic number is positive. The local note calls out:
3, 5, 6, 9, 10, 11, 12, 25, 125, 625
There are likely more; this should be derivable from the signed-div-by-constant lowering logic rather than hardcoded.
Likely fix area
llvm/lib/Target/X86/X86ISelLowering.cpp- or the DAG combine / selection logic used for signed division by constants
The backend should choose the sign source based on the sign of the magic number.
Local references
- Source note:
llvm-validation/ch10-division-by-constants/bug-sdiv-positive-magic.md - Harness:
llvm-validation/ch10-division-by-constants/ch10_sdiv_posM.ll