Code generation results are less than stellar when doing &= and |= operations on registers. If the right-hand-side (rhs) is constant, then a simple read-modify-write cycle can be performed, but it seems that this is not always done properly.
Attatched example compiles as:
$ sdcc -mpic16 -p18f66j11 --obanksel=0 --pstack-model=small --fomit-frame-pointer --optimize-cmp --optimize-df --denable-peeps --opt-code-speed --fverbose-asm --use-non-free --std-sdcc99 -I. -c -o poorcode.o poorcode.c
Removing all the optimization options or enabling --opt-code-size does not seem to have any effect.
Tested on latest sdcc version from SVN:
$ sdcc --version
SDCC : pic16/pic14 3.2.1 #8447 (Apr 1 2013) (Linux)
It seems that the detection of a constant rhs is not done properly, depending on what operators are used on the rhs. It comes to mind that it may be related to a potential size-promotion of the rhs, which then is messing with the code-generator. Casting does seem to do the trick.
The second problem is specific to the &= operation, where an explicit zero leaves a redundant load. The code will optimize to a "clrf" instruction, but a load is done regardless.
#include <pic18fregs.h>
#define poorcode() do { LATC &= (~0x0f); } while(0)
void func(void)
{
LATA |= 0x0a; // good
LATB |= ~0x05; // bad
LATA |= 0xff-0x05; // good
LATB |= (unsigned char)~0x05; // good
LATA &= 0xa0; // good
LATB &= ~0x50; // bad
LATA &= 0xff-0x50; // good
LATB &= (unsigned char)~0x05; // good
LATA &= 0; // redundand read
poorcode(); // still bad
}
The output of the above example is attached.
The redundant read with "x &= 0" could be interpreted as "you asked a read-modify-write cycle, so we give it to you". This is then related to the definition of LATx to be volatile.
Anyway, the other problems still persist.
Why isn't this line shown on the .lst?
LATA &= 0; // redundand read
Beats me! Sometimes a line gets dropped? I did not remove it (only leading and trailing lines arround the function).
Anyway, the generated code is still wrong, eventhough the LATA is declared volatile.
"LATA &= 0;" translates into:
MOVF _LATA, W
CLRF _LATA
The proper version should read:
MOVLW 0
ANDWF _LATA, F
The difference between the two is that the first allows for an interrupt to pass between the instructions, whereas the second is a true atomic read-modify-write.