Recent versions of GCC, including version 12, implement an inter-procedural analysis that fatally damages the following (GCC-only) code for a system call stub on ARM/Thumb.
typedef struct { int sender; int arg; } message;
#define syscall(op) asm volatile ("svc %0" :: "i"(op))
#define SYS_SEND 9
#define NOINLINE __attribute((noinline))
void NOINLINE send(int dest, int type, message *msg)
{
syscall(SYS_SEND);
}
void send_int(int d, int t, int v)
{
message msg;
msg.arg = v;
send(d, t, &msg);
}
The intention is that the operating system's trap handler will find the three arguments of send by accessing the saved values of the argument registers r0--r2 in the exception frame for the trap. The issue is apparently that the optimiser thinks, on looking at the body of send, that the fields of its message argument are not used. Consequently, the assignment to msg.arg in the body of send_int is deleted. This is revealed by compiling the above source with
arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -O -g -Wall -ffreestanding -c trial.c
and dumping the resulting object code.
What annotation is appropriate in order to disable this over-enthusiastic optimisation?
Giving
sendthe additional attributenoipaworks, but I am nervous about this because it seems especially non-standard, and GCC documentation describes it as provided mostly for compiler debugging. The existing attributenoinlineshould remain for the sake of earlier GCC versions that don't undertandnoipa. Unimplemented attributes are just ignored by GCC, aren't they?It also works to label the
msgbuffer insend_intas volatile (and suppress a warning by adding a cast in the call tosend). But this seems especially obscure.Adding
"memory"as a clobber on thesvcinstruction doesn't work.
It would also fix the problem if I wrote the system call stubs like send in a separate file of supporting assembly language routines, and that might be the best and most robust way forward.