arm-elf-gcc optimizer thinks swi is basically a nop?? void foo (void) { struct something s; s.randomArgument = 1; asm volatile ("mov r0,%0" : : \ "r" (&s) : \ "r0", "r12", "r14", "cc"); asm volatile ("swi 13" : : ); } If this is compiled with optimization-Os, s.randomArgument = 1 is OMITTED and the software interrupt handler sees garbage inside structure s. Why? I think it's because gcc doesn't know that swi is related to branch/link. It thinks swi doesn't need the values to which s points any more than a nop would, so why assign them in the first place? The problem is cured by making s a volatile. But does anyone know if there is a more elegant way to do this? I have been trying to hide the swi's inside function-call wrappers so the code looks like it's calling an ordinary routine. Using the volatile approach, I'd have to be sure that structure addresses are always marked volatile if they're going to be passed to a swi-based function (but not necessarily a normal function) -- ugh. I could just mark all structures that could ever possibly get passed to an SWI as volatile at the typedef level but that causes other headaches and optimization could suffer. Thanks for any wisdom anyone might have on this! --Bill
Another way to avoid the problem would be to put the swi call in its own subroutine and mark it never to be inlined. But what an annoying load of overhead that would be...
OK, I found a solution. It is awful to look at but all the other permutations I tried failed either when unoptimized, when optimized, or when gcc 4.1.0 itself crashed during compilation (!!!). static inline int Do_SWI_9 (int arg1, struct something *arg2) { register long __res ; asm volatile ("mov r0,%2\n\tmov r1,%3" : \ "=X" (*(char *)(long)arg1), "=X" (*(char *)(long)arg2) : \ "r" ((long)arg1), "r" ((long)arg2) : \ "r0", "r1"); asm volatile ("swi 9" : : : "r0", "r12", "r14", "cc"); asm volatile ("mov %0, r0" : "=r" (__res) : ); return((int)__res); } The "=X" *(char *)(long)argx operands tell the compiler that argx points to something that will be used, and this forces the optimizer to load contents to the structure before calling Do_SWI_9, rather than optimize them out in the mistaken belief that those contents are not used by Do_SWI_9. The crazy casting makes sure this works even if argx happens to be a char instead of a long or a pointer (in a more general case than the example shown here). When unoptimized, this creates some pretty awful code, as the compiler loads the values of arg1 and arg2 twice before copying them into r0 and r1; however when optimized it's better, not great, but better. I tried all sorts of more elegant approaches, including assigning the __res variable to r0 and indicating that the swi command wrote a result to __res, but none of them worked. Believe it or not the above approach was the only way I could get reliable compilation under both optimized and unoptimized conditions. Hope this helps somebody avoid the pain I just went through. --Bill
One last revision. The return value "mov" assignment can be made non-volatile and this tightens up the optimized code significantly, as it allows the optimizer to omit assignment of the return value when not used (which is the case for most of my swi calls). static inline int Do_SWI_9 (int arg1, struct something *arg2) { register long __res ; asm volatile ("mov r0,%2\n\tmov r1,%3" : \ "=X" (*(char *)(long)arg1), "=X" (*(char *)(long)arg2) : \ "r" ((long)arg1), "r" ((long)arg2) : \ "r0", "r1"); asm volatile ("swi 9" : : : "r0", "r12", "r14", "cc"); asm ("mov %0, r0" : "=r" (__res) : ); return((int)__res); } I tried the same thing with the parameter assignment, cutting it into two separate non-volatile asm statements (one for each register), but that crashed gcc. Probably time for me to quit while I'm ahead. --Bill
OK there is yet another fix. The above version doesn't work under level-2 optimization; sometimes the return argument is not provided. To fix it requires REALLY fooling gcc: static inline int Do_SWI_9 (int arg1, struct something *arg2) { register long __res asm("r0"); asm volatile ("mov r0,%2\n\tmov r1,%3" : \ "=X" (*(char *)(long)arg1), "=X" (*(char *)(long)arg2) : \ "r" ((long)arg1), "r" ((long)arg2) : \ "r0", "r1"); asm volatile ("swi 9 @ %0" : "=r" (__res) : : "r12", "r14", "cc"); return((int)__res); } What's different? (1) The __res variable is now explicitly placed in r0. (2) The swi call is marked as returning a value in __res. (3) The "@%0" comment after swi FOOLS asm() into thinking swi really does affect r0. (4) The "mov %0,r0" after the swi is omitted as it's no longer necessary. Is this voodoo or what? But it appears to work great under level 2 optimization.
Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
Log in with Google account
No account? Register here.