If you have specific code you want then...you just write it. Start with the compiled code and hand optimize as desired. The compiler cannot read your mind.
gcc 5.x.x is where gnu peaked as far as code output, it has gone down hill since. but that does not mean that version is always better than the newer versions. godbolt or just having various ones installed on your computer can help if you are trying to get the compiler to do the work for you.
unsigned int fun ( unsigned int tmp )
{
unsigned int bits;
bits=0;
for (unsigned char pos=0; pos<32; pos++)
{
if (tmp & 1)
{
bits++;
}
tmp = tmp >> 1;
}
return(bits);
}
with bits as a 32 bit
Disassembly of section .text:
00000000 <fun>:
0: 0002 movs r2, r0
2: b510 push {r4, lr}
4: 2320 movs r3, #32
6: 2000 movs r0, #0
8: 2401 movs r4, #1
a: 0021 movs r1, r4
c: 3b01 subs r3, #1
e: 4011 ands r1, r2
10: b2db uxtb r3, r3
12: 1840 adds r0, r0, r1
14: 0852 lsrs r2, r2, #1
16: 2b00 cmp r3, #0
18: d1f7 bne.n a <fun+0xa>
1a: bd10 pop {r4, pc}
r4 is set one time outside the loop
with bits as an 8 bit value
Disassembly of section .text:
00000000 <fun>:
0: 0002 movs r2, r0
2: 2320 movs r3, #32
4: 2000 movs r0, #0
6: 2101 movs r1, #1
8: 4211 tst r1, r2
a: d001 beq.n 10 <fun+0x10>
c: 3001 adds r0, #1
e: b2c0 uxtb r0, r0
10: 3b01 subs r3, #1
12: b2db uxtb r3, r3
14: 0852 lsrs r2, r2, #1
16: 2b00 cmp r3, #0
18: d1f6 bne.n 8 <fun+0x8>
1a: 4770 bx lr
r1 is set to 1 outside the loop. this one is less efficient as it has to do the utxb every loop.
naturally you would never want to use a char for a loop variable like that (nor for that counter), you want a register sized variable unless you need one larger than register sized and the you have to just incur the cost.
unsigned int fun ( unsigned int tmp )
{
unsigned int bits;
bits=0;
for (unsigned int pos=0; pos<32; pos++)
{
if (tmp & 1)
{
bits++;
}
tmp = tmp >> 1;
}
return(bits);
}
00000000 <fun>:
0: 0003 movs r3, r0
2: b510 push {r4, lr}
4: 2220 movs r2, #32
6: 2000 movs r0, #0
8: 2401 movs r4, #1
a: 0021 movs r1, r4
c: 3a01 subs r2, #1
e: 4019 ands r1, r3
10: 1840 adds r0, r0, r1
12: 085b lsrs r3, r3, #1
14: 2a00 cmp r2, #0
16: d1f8 bne.n a <fun+0xa>
18: bd10 pop {r4, pc}
that is a bit better
unsigned int fun ( unsigned int tmp )
{
unsigned int bits;
bits=0;
for (unsigned int pos=0x80000000; pos; pos>>=1)
{
if (tmp & pos)
{
bits++;
}
}
return(bits);
}
worse interestingly
unsigned int fun ( unsigned int tmp )
{
unsigned int bits;
bits=0;
for (unsigned int pos=0x1; pos; pos<<=1)
{
if (tmp & pos)
{
bits++;
}
}
return(bits);
}
not any better with this compiler.
When maybe you were looking for something like this
push {r4,lr}
mov r1,#0
mov r2,#1
mov r3,#32
top:
movs r4,r0
ands r4,r2
adds r1,r4
lsrs r0,r0,#1
subs r3,#1
bne top
mov r0,r1
pop {r4,pc}
For bit counting, but bit counting leads to some of this optimization (no need for a branch)
unsigned int fun ( unsigned int tmp, unsigned int bits )
{
for (unsigned int pos=0; pos<32; pos++)
{
if (tmp & 1)
{
bits<<=2;
}
tmp >>= 1;
}
return(bits);
}
00000000 <fun>:
0: 0003 movs r3, r0
2: 2220 movs r2, #32
4: 0008 movs r0, r1
6: 2101 movs r1, #1
8: 4219 tst r1, r3
a: d000 beq.n e <fun+0xe>
c: 0080 lsls r0, r0, #2
e: 3a01 subs r2, #1
10: 085b lsrs r3, r3, #1
12: 2a00 cmp r2, #0
14: d1f8 bne.n 8 <fun+0x8>
16: 4770 bx lr
mov r1,#1 is still outside the loop. The compiler was told to do an and and it is doing an and and maybe there is no coded optimization around the corner case of and 1 with a shift right later.
unsigned int fun ( unsigned int tmp, unsigned int bits )
{
for (unsigned int pos=0; pos<32; pos++)
{
tmp >>= 1;
if (tmp & 1)
{
bits<<=2;
}
}
return(bits);
}
This is obviously NOT functionally the same, but the compiler still uses an and (tst) here.
Would need to go look at the gcc sources to see when if ever it produces a bcc or bcs, not every instruction in an instruction set is used by a compiler, the authors have their favorite ways of doing things and the first job of the compiler is a functional equivalent. The optimizer likewise has to be functionally equivalent first, then maybe more efficient second.
OMG, okay, so I never use godbolt, and I did not see the right combinations (cortex-m), but I tried clang for armv6m and...well...they unrolled the loop for speed. with -O3
clang with -O2
Disassembly of section .text:
00000000 <fun>:
0: 2220 movs r2, #32
2: e003 b.n c <fun+0xc>
4: 1e52 subs r2, r2, #1
6: 0840 lsrs r0, r0, #1
8: 2a00 cmp r2, #0
a: d003 beq.n 14 <fun+0x14>
c: 07c3 lsls r3, r0, #31
e: d0f9 beq.n 4 <fun+0x4>
10: 0089 lsls r1, r1, #2
12: e7f7 b.n 4 <fun+0x4>
14: 4608 mov r0, r1
16: 4770 bx lr
and that is a whole other approach, you incur a lot of branches and the side effects of that (the cortex-m0+ pipe is tiny though). This could perform worse not just because of pipe stuff but because of fetching, you would need a three deep branch predictor cache, but you incur extra fetching. This is assumed to be an MCU running out of flash, and flash tends to be slow, depends heavily on the chip vendor and how fast you are running the mcu, etc. More instructions may be much faster than less instructions with more branches.
With these high performance architectures (arm, risc) you need to also take into account alignment, take the same machine code adjust it up or down one or two or three half words and it can perform tens of a percent slower (or faster) simply due to fetching. Running this code from ram instead of flash should help in general, but it depends on the chip vendor (arm is not a chip vendor) and how you are clocking things.