I'm a couple months or more into the development of an atmega324-based product, and happened across what I think is a compiler bug of a very subtle and insidious kind.
Some weeks ago, I added a "-mcall-prologues" switch to my compiler invocations, because I'm more concerned with overall codesize than execution speed, in most places. There is one aspect of the product where I'm maniacally interested in speed, though: a pair of ISRs that implement software I2C slave communications. To make them as fast as possible, they're constructed to depend on three permanently dedicated processor registers: R3, R4, and R5.
In the early stages of the project, I'd sift through the generated .lss files with a text editor to reassure myself that the generated code hadn't used any of these registers, and for quite awhile, it didn't. I've lately begun to use floating-point operations, though, and I soon saw that some of the larger routines pressed R3..R5 into service.
At that point, I grudgingly applied the remedy described by FAQ item #3 from WinAVR's installed on-line help docs: ("How to permanently bind a variable to a register?"). I set up a header file that permanently bound R3..R5 to some dummy variables, and then ensured that every source module included that header. Doing that kept even those "larger routines" from using any of my ISR-dedicated registers, or so I thought.
Today, though, I did another "sifting" inspection of my overall generated .lss file (mindful that I haven't guaranteed that the code from the external library archives I'm using - mathlib.a, etc. - abide by my rules), and found a place where R5 was used. It wasn't used by any of the library code, though; it was used by the code generated for *my* source, despite its prohibition against the use of R3..R5.
The error was that for one of my routines, the called prologue/epilogue pair "saved" and "restored" register R5, even though R5 was used nowhere within the body of the routine itself (or anywhere else in the .lss file, for that matter). In a "single-threading" execution environment, the only harm done would be squandering four machine cycles (and who'd notice?), but when the ISR depends upon being able to find the value it put in R5 midway through the execution of the bugged subroutine still there after the bugged subroutine exits, there would eventually be (as Robocop would say) ...trouble.
If you unpack the files from my test-case archive into a test folder and "make" them, you should see the problem manifest at address 0x448 in the resultant "Test.lss" listing file:
break; } } } 442: cd b7 in r28, 0x3d ; 61 444: de b7 in r29, 0x3e ; 62 446: ef e0 ldi r30, 0x0F ; 15 448: 0c 94 74 04 jmp 0x8e8 ; 0x8e8 <__epilogue_restores__+0x6>
Here is the beginning of the stock "epilogue" sequence:
000008e2 <__epilogue_restores__>: 8e2: 2a 88 ldd r2, Y+18 ; 0x12 8e4: 39 88 ldd r3, Y+17 ; 0x11 8e6: 48 88 ldd r4, Y+16 ; 0x10 8e8: 5f 84 ldd r5, Y+15 ; 0x0f 8ea: 6e 84 ldd r6, Y+14 ; 0x0e
I apologize for not making any effort to find the "minimum test case" that still exhibits the problem. Because I'd only expect it to manifest when the compiler is stressed for lots of local variables, I was pretty sure it wouldn't show up for a purely trivial program, and it was hard enough just severing all the ties to other modules so this would compile.
Here's an copy of some relevant output from my run of "make":
-------- begin -------- avr-gcc.exe (WinAVR 20100110) 4.3.3 Compiling: setColor.cpp avr-gcc.exe -c -mcall-prologues -mmcu=atmega324a -Wall -gdwarf-2 -msize -Os -funsigned-char -funsigned-bitfields -fshort-enums -I. -DF_CPU=20000000UL -MD -MP -MT setColor.o -MF dep/setColor.o.d setColor.cpp -o setColor.o
I don't expect any fixes for the problem, but I thought I would share it here, since it would be an absolute horror to track down from a "why is the application acting so funny every once in a blue moon?" standpoint.
I guess I'll just stop using the "-mcall-prologues" switch.