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
send
the additional attributenoipa
works, 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 attributenoinline
should 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
msg
buffer insend_int
as volatile (and suppress a warning by adding a cast in the call tosend
). But this seems especially obscure.Adding
"memory"
as a clobber on thesvc
instruction 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.