Я пытаюсь реализовать некоторые "OSEK-Services" на arm7tdmi-s, используя g cc arm. К сожалению, повышение уровня оптимизации приводит к «неправильному» генерированию кода. Главное, чего я не понимаю, это то, что компилятор, похоже, игнорирует стандарт вызова процедуры, например, передает параметры в функцию, перемещая их в регистры r0-r3. Я понимаю, что вызовы функций могут быть встроенными, но все же параметры должны быть в регистрах для выполнения системного вызова.
Рассмотрим следующий код, чтобы продемонстрировать мою проблему:
unsigned SysCall(unsigned param)
{
volatile unsigned ret_val;
__asm __volatile
(
"swi 0 \n\t" /* perform SystemCall */
"mov %[v], r0 \n\t" /* move the result into ret_val */
: [v]"=r"(ret_val)
:: "r0"
);
return ret_val; /* return the result */
}
int main()
{
unsigned retCode;
retCode = SysCall(5); // expect retCode to be 6 when returning back to usermode
}
Я написал программный обработчик прерываний верхнего уровня в сборке выглядит следующим образом:
.type SWIHandler, %function
.global SWIHandler
SWIHandler:
stmfd sp! , {r0-r2, lr} @save regs
ldr r0 , [lr, #-4] @load sysCall instruction and extract sysCall number
bic r0 , #0xff000000
ldr r3 , =DispatchTable @load dispatchTable
ldr r3 , [r3, r0, LSL #2] @load sysCall address into r3
ldmia sp, {r0-r2} @load parameters into r0-r2
mov lr, pc
bx r3
stmia sp ,{r0-r2} @store the result back on the stack
ldr lr, [sp, #12] @restore return address
ldmfd sp! , {r0-r2, lr} @load result into register
movs pc , lr @back to next instruction after swi 0
Таблица диспетчеризации выглядит следующим образом:
DispatchTable:
.word activateTaskService
.word getTaskStateService
Функция SystemCall выглядит следующим образом:
unsigned activateTaskService(unsigned tID)
{
return tID + 1; /* only for demonstration */
}
работает без оптимизации, все работает нормально, а параметры находятся в регистрах, как и ожидалось: см. Следующий код с оптимизацией -O0:
00000424 <main>:
424: e92d4800 push {fp, lr}
428: e28db004 add fp, sp, #4
42c: e24dd008 sub sp, sp, #8
430: e3a00005 mov r0, #5 @move param into r0
434: ebffffe1 bl 3c0 <SysCall>
000003c0 <SysCall>:
3c0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
3c4: e28db000 add fp, sp, #0
3c8: e24dd014 sub sp, sp, #20
3cc: e50b0010 str r0, [fp, #-16]
3d0: ef000000 svc 0x00000000
3d4: e1a02000 mov r2, r0
3d8: e50b2008 str r2, [fp, #-8]
3dc: e51b3008 ldr r3, [fp, #-8]
3e0: e1a00003 mov r0, r3
3e4: e24bd000 sub sp, fp, #0
3e8: e49db004 pop {fp} ; (ldr fp, [sp], #4)
3ec: e12fff1e bx lr
Компиляция того же кода с -O3 приводит к следующей сборке код:
00000778 <main>:
778: e24dd008 sub sp, sp, #8
77c: ef000000 svc 0x00000000 @Inline SystemCall without passing params into r0
780: e1a02000 mov r2, r0
784: e3a00000 mov r0, #0
788: e58d2004 str r2, [sp, #4]
78c: e59d3004 ldr r3, [sp, #4]
790: e28dd008 add sp, sp, #8
794: e12fff1e bx lr
Обратите внимание, как systemCall вставляется без присваивания значения 5 t0 r0.
Мой первый подход состоит в том, чтобы вручную переместить эти значения в регистры, адаптировав функцию SysCall сверху следующим образом:
unsigned SysCall(volatile unsigned p1)
{
volatile unsigned ret_val;
__asm __volatile
(
"mov r0, %[p1] \n\t"
"swi 0 \n\t"
"mov %[v], r0 \n\t"
: [v]"=r"(ret_val)
: [p1]"r"(p1)
: "r0"
);
return ret_val;
}
В этом минимальном примере это работает, но я не совсем уверен является ли это наилучшей возможной практикой. Почему компилятор считает, что он может опустить параметры при вставке функции? Есть ли у кого-нибудь предложения по поводу того, подходит ли этот подход или что нужно делать по-другому?
Заранее спасибо