So I had an interview question before regarding bit manipulation. The company is a well known GPU company. I had very little background in assembly language (weird despite being a phd student in computer architecture) and as this narrative would indicate, I botched it. The question was a simple:
"Write a fast code that will count the number of 1's in a 32-bit register."
Now I am in the process of studying arm assembly. So naturally I revisited this problem again and have come up with this code just by studying the ISA.
For you arm experts out there, is this correct? Is there a faster way to do this? Being a beginner, I naturally think this is incomplete. The AND instruction in "xx" feels redundant but there is no other way to shift a register in ARM isa...
R1 will contain the number of bits at the end while R2 is the register with bits we want to count. r6 is just a dummy register. Comments are enclosed in ()
MOV R1, #0 (initialize R1 and R6 to zero)
MOV R6, #0
xx: AND R6, R6, R2, LSR #1 (Right shift by 1, right most bit is in carry flag)
ADDCS R1, #1 (Add #1 to R1 if carry flag is set)
CMP R2, #0 (update the status flags if R2 == 0 or not)
BEQ xx (branch back to xx until R2==0)
long count_bits_long (long);
Best references for bit hacks are
Bit Twiddling Hacks
page saysThen I would suggest you to use
gcc
andobjdump
(or this great online gcc tool) to see how this high level snippet would look like as arm instructions.So it looks like this gives you result in
12
instructions, which roughly can translate to same amount of cycles.Comparing integer twiddling above to
look up table
approach used by libgcc, look up table should be even slower considering extra memory accesses.If this code is fast or not depends on the processor. For sure it will be not very fast on Cortex-A8 but may run very fast on Cortex-A9 and newer CPU.
It is however a very short solution.
Expects input in r0, and returns output in r0
The main work is done in the vcnt.8 instruction which counts the bits of each byte in a NEON register and stores the bitcount back into the bytes of D0.
There is no
vcnt.32
form, only.8
, so you need to horizontally add the 4 bytes together, which is what the rest of the code is doing.Since this is tagged ARM, the
clz
instruction is most helpful. The problem is also described as a population count.gcc
has __builtin_popcount() for this. As does the ARM tools. There is this link (don't feel bad about your solution, some one made a web page with nearly the same) and also there is Dave Seal's version with six instruction for non-clz
ARMs. Theclz
is advantageous and can be used to produce a faster algorithm, depending on the input.As well as auselen's good reading suggestion,Hacker's Delight this bit twiddling blog maybe helpful which is talking about such things in a graphic context. At least I found it useful to understand some of Qt's blitting code. However, it has some usefulness in coding a population count routine.
The
carry add
unit is useful in a divide and conquer sense, making the problemO(ln n)
.clz
is more useful if the data has runs of ones or zeros.The Hacker's Delight entry has more background on Dave Seal's ARM code.
You could use a precomputed look up table and reduce the number of iterations to 2 or 4.
You could also use the logarithmic approach.
For more info see this Wikipedia article.
This will do it, r3 is just a dummy register with 0 to make ADC work properly.