Bug: InstCombine misses (x & ~C) - (x & C) → (x ^ C) - C
Title
InstCombine: (x & ~C) - (x & C) should simplify to (x ^ C) - C for any constant C
Summary
When a word is split into complementary bit groups and the groups are subtracted, InstCombine does not fold the expression to the algebraically simpler 2-operation form. This manifests concretely in the Schroeppel negabinary-to-binary conversion (§12-1 of Hacker’s Delight), where three equivalent forms generate 4, 5, and 2 instructions respectively, but only the last form is written in the 2-operation canonical style.
Reproducer
; Form 1: (N & 0x55555555) − (N & 0xAAAAAAAA) [4 insns]
define i32 @negabin2bin_form1(i32 %n) {
%a = and i32 %n, 1431655765 ; 0x55555555
%b = and i32 %n, -1431655766 ; 0xAAAAAAAA (= ~0x55555555)
%r = sub i32 %a, %b
ret i32 %r
}
; Form 2: N − ((N & 0xAAAAAAAA) << 1) [5 insns]
define i32 @negabin2bin_form2(i32 %n) {
%a = and i32 %n, -1431655766
%s = shl i32 %a, 1
%r = sub i32 %n, %s
ret i32 %r
}
; Form 3: (N ^ 0xAAAAAAAA) − 0xAAAAAAAA [2 insns, already optimal]
define i32 @negabin2bin_form3(i32 %n) {
%x = xor i32 %n, -1431655766
%r = sub i32 %x, -1431655766
ret i32 %r
}opt -S -passes='default<O2>' | llc -O2 -mtriple=x86_64-pc-linux-gnuCurrent output
negabin2bin_form1: ; 4 instructions
movl %edi, %eax
andl $1431655765, %eax
andl $-1431655766, %edi
subl %edi, %eax
retq
negabin2bin_form2: ; 5 instructions
movl %edi, %eax
movl %edi, %ecx
andl $715827882, %ecx
addl %ecx, %ecx
subl %ecx, %eax
retq
negabin2bin_form3: ; 2 instructions (already optimal)
xorl $-1431655766, %edi
leal 1431655766(%rdi), %eax
retqExpected output (forms 1 and 2 should equal form 3)
negabin2bin_form1: ; 2 instructions
xorl $-1431655766, %edi
leal 1431655766(%rdi), %eax
retqWhy they are equivalent
Let C = 0xAAAAAAAA, E = x & ~C (even-index bits), O = x & C (odd-index bits).
- Form 1:
E − O - Form 2:
(E + O) − 2O = E − O - Form 3:
(x ^ C) − C = (C − O + E) − C = E − O✓ (XOR with C flips the C-bit positions: O → C − O, since O ⊆ C)
Verified exhaustively for 32-bit integers.
Missing InstCombine rules
Rule A: (x & ~C) - (x & C) ==> (x ^ C) - C
Rule B: x - ((x & C) << 1) ==> (x ^ C) - C
(applicable when C & 1 == 0, so that the shift stays within C’s bit positions;
here C = 0xAAAAAAAA has bit 0 clear)
Rule A is the cleaner and more general case. The left-hand side of Rule A is exactly the pattern for “sum of even-position bits minus sum of odd-position bits,” which arises in negabinary arithmetic, base-4 digit extraction, and several other bit-manipulation contexts.
Where to fix
llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp or
InstCombineAndOrXor.cpp: add a fold for (x & C1) - (x & C2) when C1 & C2 == 0
and C1 | C2 == allones (i.e., C1 and C2 partition the bits) → (x ^ C2) - C2.