Прямой переход к другой функции C ++ - PullRequest
6 голосов
/ 29 марта 2010

Я портирую небольшую академическую ОС с 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 (a.k.a. JUMP) вместо CALL, оставляя стек и CSA неизменными при переходе к (в остальном нормальному) функции C ++ superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to).

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

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

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 ++ будет одним из способов, подсказка для исправления кода asm будет другим. Другой способ, возможно, заключался бы в сообщении компилятору, что a1..a3 уже должен быть в регистрах r0..r2 при вводе оператора asm (я немного разбирался в этом, но не нашел ни одной подсказки).

Ответы [ 2 ]

1 голос
/ 29 марта 2010

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

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

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

0 голосов
/ 31 марта 2010

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

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

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

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

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

...