I have found what looks suspiciously like a compiler bug: in some situations, 20-bit pointers are not written correctly to the stack when assembling varargs lists.
Here is the test code:
typedef unsigned int uint16_t; extern void func(const char* format, ...); extern uint16_t var; void test(void) { func("dummy", (void*)var); }
If this is built with:
msp430-elf-gcc -S -Os -mlarge bug.c
...then the resulting assembly looks like this:
test: SUBA #8, R1 MOVX.A #.LC0, @R1 MOVX.W &var, R12 MOVX R12, 4(R1) CALLA #func ADDA #8, R1 RETA
You can see that var is loaded into r12 with a movx.w instruction, which is correct --- var is a uint16_t. But then it's written back to the stack using a bare movx instruction, which without a qualifier defaults to movx.w. (As confirmed by objdump.) This should be a movx.a.
As it's allocating four bytes for that stack slot, I think it likely that the size suffix has been accidentally dropped off the end of the instruction in the gcc instruction template for 'extend 16-bit word to 20-bit word and store into memory'. If I rearrange the code to force the extension to happen in a separate instruction, it works fine.
The gcc version is:
msp430-elf-gcc (GCC) 4.9.1 20140707 (prerelease (msp430-14r1-167)) (GNUPro 14r1) (Based on: GCC 4.8 GDB 7.7 Binutils 2.24 Newlib 2.1)
Can anyone confirm this? And does anyone know of a workaround or the availability of a fixed compiler?