2010-03-29 5 views
6

Ich portiere ein kleines akademisches Betriebssystem von TriCore zu 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 ändern.Direkt zu einer anderen C++ Funktion wechseln

Auf TriCore (oder vielmehr auf tricore-g ++), diese Vorlage Wrapper (für all drei Argument-Funktion) arbeitet:

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 Assemblerbefehl J (aka JUMP) erzeugt statt von CALL, wobei der Stapel und CSAs unverändert bleiben, wenn zu der (ansonsten normalen) C++ Funktion superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to) gesprungen wird.

Jetzt brauche ich ein gleichwertiges Verhalten auf ARM Cortex (oder, besser gesagt, für Arm-ohne-linux-gnueabi-g ++), dh erzeugen eine B (aka Zweig) Anweisung statt BLX (aka Zweig mit Link und Austausch) . Aber es gibt kein interrupt_handler Attribut für arm-g ++ und ich konnte kein äquivalentes Attribut finden.

Also versuchte ich direkt an asm volatile und Schreiben des asm-Code zurückgreifen:

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 erfordert, dass Funktionsargumente in den Registern übergeben werden, d. H. In diesem Fall r0..r2, so dass es funktionieren sollte.

Aber dann stirbt der Linker mit

undefined reference to `r6' 

auf der schließenden Klammer der asm-Anweisung ... und ich weiß nicht, was daraus zu machen. OK, ich bin nicht der Experte in C++, und die ASM-Syntax ist nicht sehr einfach ... Hat also jemand einen Tipp für mich? Ein Hinweis auf die korrekte __attribute__ für Arm-G ++ wäre eine Möglichkeit, ein Hinweis zur Behebung des Asm-Codes wäre ein anderer. Eine andere Möglichkeit wäre vielleicht, dem Compiler zu sagen, dass a1..a3 bereits in den Registern r0..r2 sein sollte, wenn die asm-Anweisung eingegeben wird (ich habe das ein wenig untersucht, aber keinen Hinweis gefunden).

+0

Sind a1, a2, a3 ein Zeiger? versuche, sie in '(void *)' zu konvertieren – osgx

+0

Das Attribut für einen ARM-Interrupt-Handler ist 'interrupt'. Siehe http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html –

+0

@Mike: Nein, dieses Attribut erzeugt immer noch einen 'BLX' Befehl ... – orithena

Antwort

0

Nun, ich habe jetzt herausgefunden, was schief gelaufen ist. Das gesamte Konzept des JUMPs direkt auf eine andere Funktion ist bei ARM Cortex nicht möglich, da TriCore einen Context Save Area (CSA) verwendet, um den gesamten CPU-Kontext zu speichern, wenn Sie eine andere Funktion aufrufen. Betrachten Sie es als einen zweiten, unabhängigen Stapel, der mit jedem CALL wächst und mit jedem RET schrumpft. Und jeder CSA-Block hat eine konstante Größe.

ARM Cortex dagegen verwendet einen einfachen Standardstack (ok, er kennt einen Systemstack und einen Threadstack, aber das ist hier unwichtig) - und GCC speichert nur, was es für jede Funktion benötigt Jeder Rahmen hat eine andere Größe. Es ist daher nicht möglich, zu einer anderen Funktion zu springen, da der Stack beschädigt wird, sobald die Funktion zum Springen die nichtflüchtigen Register speichert.

Und über den Linker-Fehler mit der undefinierten Verweis auf r6 ... nun, ich hätte die Dokumentation der Anleitung besser lesen sollen. B ist eine unbedingte Verzweigung zu einer sofortigen Adresse, BX ist die Anweisung, die die Verzweigungsadresse in einem Register erwartet. Ich wurde von der Anweisungsliste im Handbuch getäuscht, wo BX kurz als "Verzweigung mit Austausch" beschrieben wurde. Ich wollte nichts austauschen, ich wollte einen einfachen Sprung, also habe ich nicht weiter gelesen.

Also, nach dem Austausch B mit BX in der asm volatile Code, kompiliert der Code. 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 auf die klassische Funktion zurückgreifen, die jetzt ruft ...

1

Der Verbindungsfehler wird verursacht, indem versucht wird, den Verzweigungsbefehl zu verwenden, um zu einem Zeiger zu springen. Dies erzeugt Code wie b r6, der nicht verknüpfen kann, weil r6 kein Symbol ist. Ändern Sie die Verzweigungsinstruktion zu mov pc,%0, und Sie sollten den richtigen Sprung erhalten.

Wie ich in den Kommentaren erwähnt, ARM-Interrupt-Handler werden mit einem interrupt Attribut deklariert, aber wie Sie gefunden, hat dies keine Auswirkungen, wie sie genannt werden. Ich denke, das war ein plattformspezifischer Trick, der bei TriCore das Richtige getan hat.

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