Hi,
When mixing C and inline assembly, SDCC optimizes away statements even tough it shouldn't.
Example:
__bit foo = 0; int main() { foo = 1; // this line is optimized away even if it shouldn't! __asm jb _foo, somelabel __endasm; foo = 0; // because SDCC thinks this assignment makes the first one unnecessary __asm somelabel: __endasm; // foo should be 1 one, but is actually 0 because SDCC optimized away the first assigment to foo return foo; }
The resulting assembly code looks like this:
; ----------------------------------------- ; function main ; ----------------------------------------- _main: ar7 = 0x07 ar6 = 0x06 ar5 = 0x05 ar4 = 0x04 ar3 = 0x03 ar2 = 0x02 ar1 = 0x01 ar0 = 0x00 ; test.c:9: __endasm; jb _foo, somelabel ; test.c:11: foo = 0; clr _foo ; test.c:15: __endasm; somelabel: ; test.c:19: return foo; mov dptr,#0x0000 ret
Clearly, there is a setb _foo
instruction missing.
new asm() format results in the same behaviour:
__asm__("jb _foo, somelabel");
Inserting a function call between the two assignments forces correct behaviour of the optimizer.
Compile the attached test.c
with sdcc -c test.c
**$ sdcc-sdcc --version** SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8 3.5.0 #9253 (Jul 4 2015) (Linux) published under GNU General Public License (GPL)
Make foo volatile and it will be ok.
I wonder what others think of this. Should inline asm be treated as a function call which can make all sorts of side effects or not? It could inhibit optimizations that the inline asm was supposed to bring as well. My take is that with inline asm comes a responsibility to take full control.
Using volatile is an excellent suggestion and indeed alleviates the problem.
I would suggest to at least make note of this kind of issue in the SDCC manual.
I concur that using inline assembly makes optimization your responsibility; At least in my case, that is exactly why I'm using it. I want to have as much control over the generated code as possible without the compiler interfering.
No, the compiler would be forced to generate poor code which would wipe out any gains. The way it is now is the right way I think. The compiler builds code around the inlined asm as if it weren't there and the peepholer assumes all registers are live leading into an inlined asm block unless it's allowed to look into the inlined asm via compile line argument. What we are really talking about is inlining a few instructions to access hw features not (efficiently?) available from C (disabling interrupts might be an example). If there is a significant amount of inlined asm, the function should be completely written in asm in the first place.
Well, at least the poor code would be correct ;)
And no, we are not talking about inlining a few instructions (in which case I would more or less agree with you). At least I'm not. I have a project here where I have a few hundred lines of hand-optimized assembly for the hot path of some signal processing stuff. The rest of this function where speed is less essential is written in C as I find that much less cumbersome to write. I suppose, I could move these parts to separate functions, but that would incur additional overhead for the call and program flow.
I find it very conventient to mix assembly and C, using the former when it matters and falling back to easier to read and write C when the generated code is good enough.
How about a pragma to disable any optimizations? In my example, I really do not want the compiler to anything more fancy than simply assembling my instructions.