Вопрос

Я портирую небольшую академическую ОС с TriCore на ARM Cortex (набор инструкций Thumb-2).Чтобы планировщик работал, мне иногда нужно перейти непосредственно к другой функции, не изменяя ни стек, ни регистр связи.

На TriCore (или, скорее, на tricore-g++) работает этот шаблон-обертка (для любой функции с тремя аргументами):

template< class A1, class A2, class A3 > 
inline void __attribute__((always_inline)) 
JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) {
    typedef void (* __attribute__((interrupt_handler)) Jump3)( A1, A2, A3);
    ( (Jump3)func )( a1, a2, a3 );
}

//example for using the template:
JUMP3( superDispatch, this, me, next );

Это сгенерирует инструкцию ассемблера J (он жеПРЫГАТЬ) вместо CALL, оставляя стек и CSA неизменными при переходе к (в противном случае обычной) функции C++ superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to).

Теперь мне нужно эквивалентное поведение на ARM Cortex (вернее, на Arm-none-linux-gnueabi-g++), т.е.создать B (он жеBRANCH) инструкция вместо BLX (он жеВЕТКА со ссылкой и обменом).Но нет interrupt_handler атрибут для руки-g++, и я не смог найти эквивалентного атрибута.

Поэтому я попытался прибегнуть к asm volatile и написание ассемблерного кода напрямую:

template< class A1, class A2, class A3 > 
inline void __attribute__((always_inline)) 
JUMP3( void (*func)( A1, A2, A3), A1 a1, A2 a2, A3 a3 ) {
    asm volatile (
                  "mov.w r0, %1;"
                  "mov.w r1, %2;"
                  "mov.w r2, %3;"
                  "b %0;"
                            :
                            : "r"(func), "r"(a1), "r"(a2), "r"(a3)
                            : "r0", "r1", "r2"
                  );
}

Пока все хорошо, по крайней мере, в моей теории.Thumb-2 требует, чтобы аргументы функции передавались в регистрах, т.е.в данном случае r0..r2, так что должно работать.

Но затем компоновщик умирает с

undefined reference to `r6'

в закрывающей скобке оператора asm...и я не знаю, что с этим делать.Хорошо, я не эксперт в C++, а синтаксис asm не очень прост...так есть ли у кого-нибудь подсказка для меня?Подсказка к правильному __attribute__ для Arm-g++ это один способ, а подсказка по исправлению ассемблерного кода - другой.Другой способ - сообщить компилятору, что a1..a3 уже должно быть в реестрах r0..r2 когда вводится оператор asm (я немного разбирался в этом, но не нашел подсказки).

Это было полезно?

Решение 2

Ну, теперь я понял, что пошло не так.

Вся концепция перехода непосредственно к другой функции является спорной для ARM Cortex, поскольку TriCore использует область сохранения контекста (CSA) для сохранения всего контекста ЦП каждый раз, когда вы вызываете другую функцию.Думайте об этом как о втором независимом стеке, который растет с каждым CALL и сжимается с каждым RET.И каждый блок CSA имеет постоянный размер.

ARM Cortex, с другой стороны, использует простой стандартный стек (хорошо, он знает о системном стеке и стеке потоков, но здесь это неважно) - и GCC просто сохраняет то, что ему нужно для каждой функции, поэтому каждый кадр имеет другой размер.Поэтому о простом переходе к другой функции не может быть и речи, поскольку стек будет поврежден, как только функция перехода начнет сохранять энергонезависимые регистры, которые она использует.

А по поводу ошибки компоновщика с неопределенной ссылкой на r6...ну, мне следовало более внимательно прочитать документацию по набору инструкций. B является безусловной ветвью немедленный адрес, BX это инструкция, которая ожидает адрес ветки в регистре.Меня обманул список инструкций в руководстве, где BX был кратко описан как «Отделение с обменом».Я не хотел ничего разменивать, мне хотелось простого прыжка, поэтому дальше читать не стал.

Итак, после обмена B с BX в asm volatile код, код скомпилирован.Но, как отмечалось выше, вся концепция не может работать так, как ожидалось.Может быть, кто-то еще сможет найти вариант использования этого кода, мне придется теперь прибегнуть к классическому вызову функций...

Другие советы

Ошибка связи вызвана попыткой использовать инструкцию ветвления для перехода к указателю.Это генерирует код типа b r6, который не может связать, потому что r6 не является символом.Измените инструкцию ветвления на mov pc,%0, и у вас должен получиться правильный прыжок.

Как я уже упоминал в комментариях, обработчики прерываний ARM объявляются с помощью interrupt атрибут, но, как вы обнаружили, это не влияет на то, как они называются.Я думаю, это был трюк, специфичный для платформы, который оказался правильным на TriCore.

Вы можете попробовать объявить переменные в определенных регистрах, используя расширенный синтаксис GCC, register int reg0 asm("r0") = a1; а не изменчивый mov инструкции.Это может позволить компилятору генерировать более качественный код.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top