Hi,
It's been years since last time I was here, so I'll need to figure out how to use this new interface... :-D
For some reason (looong story), I started to look at code efficiency, regarding copying of a bit from one bitfield to another.
In some existing code, I made this example:
struct tTestBitfield { unsigned char Bit0:1; unsigned char Bit1:1; unsigned char Bit2:1; unsigned char Bit3:1; unsigned char Bit4:1; unsigned char Bit5:1; unsigned char Bit6:1; unsigned char Bit7:1; }; struct tTestBitfield TestSource; struct tTestBitfield TestDestination; (blah-blah... TestDestination.Bit3=TestSource.Bit0;
I'm programming an XMEGA, A1 series, using AtmelStudio 7 (version 7.0.1645). Optimization is set to -Os. (Changing the optimization level made no difference.)
TestSource is copied from another variable, so the compiler can "see" that it is being loaded with something. TestDestination is used as a data byte in a binary, serial transmission, so the compiler won't just skip it.
Now, what I expect to see, is the contents of bit 0 in TestSource being copied to bit 3 in TestDestination. And it works nicely.
However, when the source is one of the other bits, some seemingly superfluous instructions are added, taking additional time when running.
This is the compiled result from the original:
TestDestination.Bit3=TestSource.Bit0; 3e38: 80 91 02 2c lds r24, 0x2C02 ; 0x802c02 <TestSource> 3e3c: 20 91 3e 2a lds r18, 0x2A3E ; 0x802a3e <TestDestination> 3e40: 80 fb bst r24, 0 3e42: 23 f9 bld r18, 3 3e44: 20 93 3e 2a sts 0x2A3E, r18 ; 0x802a3e <TestDestination>
This is pretty simple. Load both bytes into registers, grab bit 0 from the source, put it into bit 3 in the destination, store destination. I find it difficult to make a faster version myself.
But now...
TestDestination.Bit3=TestSource.Bit1; 3e38: 80 91 02 2c lds r24, 0x2C02 ; 0x802c02 <TestSource> 3e3c: 86 95 lsr r24 3e3e: 81 70 andi r24, 0x01 ; 1 3e40: 20 91 3e 2a lds r18, 0x2A3E ; 0x802a3e <TestDestination> 3e44: 80 fb bst r24, 0 3e46: 23 f9 bld r18, 3 3e48: 20 93 3e 2a sts 0x2A3E, r18 ; 0x802a3e <TestDestination>
Now, the source bit is bit 1.
The source byte is shifted right, even though bst r24,1 should work perfectly. And the andi r24, 0x01 instruction simply doesn't do anything useful. The seven bits that are masked out, aren't used anyway, so blanking them isn't necessary.
According to this site, bst (and bld) should work with all registers and all bits: https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_BST.html
It gets more weird:
TestDestination.Bit3=TestSource.Bit2; 3e38: 80 91 02 2c lds r24, 0x2C02 ; 0x802c02 <TestSource> 3e3c: 82 fb bst r24, 2 3e3e: 88 27 eor r24, r24 3e40: 80 f9 bld r24, 0 3e42: 20 91 3e 2a lds r18, 0x2A3E ; 0x802a3e <TestDestination> 3e46: 80 fb bst r24, 0 3e48: 23 f9 bld r18, 3 3e4a: 20 93 3e 2a sts 0x2A3E, r18 ; 0x802a3e <TestDestination>
Here, bst and bld are used for moving bit 2 to bit 0, before copying it to the destination bit.
A similar approach is used for bits 3, 5, 6, and 7.
This one, I find particularly creative. It was also the first oddity I encountered:
TestDestination.Bit3=TestSource.Bit4; 3e38: 80 91 02 2c lds r24, 0x2C02 ; 0x802c02 <TestSource> 3e3c: 82 95 swap r24 3e3e: 81 70 andi r24, 0x01 ; 1 3e40: 20 91 3e 2a lds r18, 0x2A3E ; 0x802a3e <TestDestination> 3e44: 80 fb bst r24, 0 3e46: 23 f9 bld r18, 3 3e48: 20 93 3e 2a sts 0x2A3E, r18 ; 0x802a3e <TestDestination>
Here, swap is used, to move bit 4 to bit 0. As in the other examples (except the first with bit 0), the other bits are thoroughly cleared.
I can imagine only one reason for the compiler to build my code this way.
My guess is, that the compiler splits the statement into two parts. First, the program needs to isolate bit n, and keep it in a location where it can find it again. Apparently, this must always be in bit 0. Only after this is done, the compiler starts looking at the destination variable. This action is almost completely split from the first part, so the compiler must rely on the bit being bit 0.
Do you people have any other ideas about this? Is there something I've missed, or is this behaviour expected?
I've tried searching the site for other posts about it, but haven't found anything.
Br, ErikT