7 Stimmen

Direkt zu einer anderen C++-Funktion springen

Ich portiere ein kleines akademisches Betriebssystem von TriCore auf ARM Cortex (Thumb-2 Befehlssatz). Damit der Scheduler funktioniert, muss ich manchmal direkt zu einer anderen Funktion springen, ohne den Stack oder das Link-Register zu verändern.

Auf TriCore (oder besser gesagt, auf tricore-g++) funktioniert diese Wrapper-Vorlage (für jede Drei-Argument-Funktion):

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 );

Dies würde den Assembler-Befehl erzeugen J (auch bekannt als JUMP) anstelle von CALL , wobei der Stack und die CSAs beim Sprung zur (ansonsten normalen) C++-Funktion unverändert bleiben superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to) .

Jetzt brauche ich ein gleichwertiges Verhalten auf ARM Cortex (oder besser gesagt, für arm-none-linux-gnueabi-g++), d.h. eine B (auch bekannt als BRANCH-Befehl) anstelle von BLX (a.k.a. BRANCH mit Link und Austausch). Aber es gibt keine interrupt_handler Attribut für arm-g++ und ich konnte kein gleichwertiges Attribut finden.

Also habe ich versucht, auf asm volatile und den asm-Code direkt zu schreiben:

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"
                  );
}

So weit, so gut, zumindest in meiner Theorie. Thumb-2 verlangt, dass Funktionsargumente in den Registern übergeben werden, d.h. r0..r2 in diesem Fall, also sollte es funktionieren.

Aber dann stirbt der Linker mit

undefined reference to `r6'

auf der schließenden Klammer der asm-Anweisung ... und ich weiß nicht, was ich davon halten soll. OK, ich bin kein Experte in C++, und die asm-Syntax ist nicht sehr einfach... hat also jemand einen Tipp für mich? Ein Hinweis auf die richtige __attribute__ für arm-g++ wäre eine Möglichkeit, ein Hinweis auf die Korrektur des asm-Codes eine andere. Ein anderer Weg wäre vielleicht, dem Compiler zu sagen, dass a1..a3 sollte sich bereits in den Registern befinden r0..r2 wenn die asm-Anweisung eingegeben wird (ich habe das ein wenig untersucht, aber keinen Hinweis gefunden).

1voto

Mike Seymour Punkte 242473

Der Verknüpfungsfehler wird durch den Versuch verursacht, mit der Verzweigungsanweisung zu einem Zeiger zu springen. Dies erzeugt Code wie b r6 die nicht verlinkt werden kann, weil r6 ist kein Symbol. Ändern Sie die Verzweigungsanweisung in mov pc,%0 und Sie sollten den richtigen Sprung erhalten.

Wie ich in den Kommentaren erwähnt habe, werden ARM-Interrupt-Handler mit einer interrupt Attribut, aber wie Sie festgestellt haben, hat dies keinen Einfluss darauf, wie sie aufgerufen werden. Ich schätze, das war ein plattformspezifischer Trick, der zufällig auf TriCore das Richtige tat.

Sie könnten versuchen, Variablen in bestimmten Registern zu deklarieren, indem Sie die erweiterte Syntax des GCC verwenden, register int reg0 asm("r0") = a1; eher als flüchtig mov Anweisungen. Dies könnte es dem Compiler ermöglichen, besseren Code zu erzeugen.

0voto

orithena Punkte 1346

Nun, ich habe jetzt herausgefunden, was schief gelaufen ist.

Das ganze Konzept des JUMPs direkt zu einer anderen Funktion ist auf dem ARM Cortex überflüssig, da TriCore eine Context Save Area (CSA) verwendet, um den gesamten CPU-Kontext jedes Mal zu speichern, wenn Sie eine andere Funktion aufrufen. Stellen Sie sich das wie einen zweiten, unabhängigen Stack vor, der mit jeder Funktion wächst. CALL und schrumpft mit jeder RET . Und jeder CSA-Block hat eine konstante Größe.

ARM Cortex hingegen verwendet einen einfachen Standard-Stack (ok, er kennt einen System-Stack und einen Thread-Stack, aber das ist hier unwichtig) -- und GCC speichert nur das, was er für jede Funktion braucht, so dass jeder Frame eine andere Größe hat. Einfach zu einer anderen Funktion zu springen kommt daher nicht in Frage, da der Stack beschädigt wird, sobald die angesprungene Funktion beginnt, die von ihr verwendeten nichtflüchtigen Register zu speichern.

Und was den Linker-Fehler mit dem undefinierten Verweis auf r6 angeht ... nun, ich hätte die Dokumentation des Befehlssatzes sorgfältiger lesen sollen. B ist eine unbedingte Verzweigung zu einer sofort Adresse, BX ist der Befehl, der die Verzweigungsadresse in einem Register erwartet. Ich habe mich von der Befehlsliste im Handbuch täuschen lassen, wo BX wurde kurz als "Branche mit Umtausch" bezeichnet. Ich wollte nichts umtauschen, ich wollte einen einfachen Sprung, also habe ich nicht weiter gelesen.

Also, nach dem Austausch B avec BX en el asm volatile Code, der Code kompiliert. Aber, wie oben erwähnt, kann das ganze Konzept nicht wie erwartet funktionieren. Vielleicht kann jemand anderes einen Anwendungsfall für diesen Code finden, ich muss jetzt auf den klassischen Funktionsaufruf zurückgreifen ...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X