Closed
Description
While looking into optimizing ed25119 verification on ARM64, I noticed that x * 19
is not optimized into shifted adds.
func multiply19(x uint64) uint64 {
return x * 19
}
// which compiles to:
MOVD $19, R1
MUL R1, R0, R0
This can be reduced to:
ADD R0<<3, R0, R1
ADD R1<<1, R0, R0
The general form seems to be:
x * c, where
c = 1 + 2^N + 2^(N+M), N != M;
N > 1, M > 1 // not sure whether this restriction is necessary
Then the multiplication can be rewritten as:
x + (x + x << M) << N
This can be checked with:
(c-1)&1 == 0 && bits.OnesCount(c - 1) == 2
Which holds for numbers like:
7, 11, 13, 19, 21, 25, 35, 37, 41, 49, 67, 69, 73, 81, 97, 131, 133, 137, 145, 161, 193...
There is also a similar reduction, that can be done:
x * c, where
c = 1 + 2^N + 2^M + 2^(N+M), N < M
N > 1 // not sure whether this restriction is necessary
Then the multiplication can be rewritten as:
x = x + x<<N; x = x + x<<M
This can be checked with:
(c-1)&1 == 0 && bits.OnesCount(c - 1) == 3 && highbit - lowbit = midbit
Which holds for numbers like:
15, 23, 27, 29, 39, 43, 51, 53, 57, 71, 75, 83, 89, 99, 101, 135, 139, 147, 163, 169, 177, 195...
I didn't verify, but this might be useful on amd64 as well.
I can send a CL about this, but I'm not sure whether there are some corner cases with high c
values that I'm not thinking of. Similarly, I wasn't able to figure out how to write the second reduction in SSA rules.