2010-03-29 22 views
6

Estoy portando un pequeño sistema operativo académico de TriCore a ARM Cortex (conjunto de instrucciones Thumb-2). Para que el programador funcione, a veces necesito SALTAR directamente a otra función sin modificar la pila ni el registro de enlace.Directamente Saltar a otra función de C++

En TriCore (o, más bien, en tricore-g ++), esta plantilla envoltorio (por cualquiera de las tres-argumento-función) funciona:

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

Esto generaría la instrucción ensamblador J (también conocido como SALTO) en lugar de CALL, dejando sin cambios la pila y CSA al pasar a la función C++ (de lo contrario, normal) superDispatch(SchedulerImplementation* obj, Task::Id from, Task::Id to).

Ahora necesito un comportamiento equivalente en ARM Cortex (o, más bien, para el brazo-ninguno-linux-gnueabi-g ++), es decir, generar una instrucción de B (aka RAMA) en lugar de BLX (aka rama con enlace y cambio) . Pero no existe el atributo interrupt_handler para arm-g ++ y no pude encontrar ningún atributo equivalente.

Así que trataron de recurrir a asm volatile y escribir el código asm directamente:

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

Hasta ahora, todo va bien, en mi teoría, al menos. Thumb-2 requiere que los argumentos de función se pasen en los registros, es decir, r0..r2 en este caso, por lo que debería funcionar.

Pero entonces el enlazador muere con

undefined reference to `r6' 

en el soporte de cierre del estado asm ... y no sé qué hacer con él. OK, no soy el experto en C++, y la sintaxis asm no es muy sencilla ... ¿alguien me ha dado alguna pista? Una sugerencia al __attribute__ correcto para arm-g ++ sería de una manera, una sugerencia para arreglar el código de asm sería otra. Otra forma podría ser decirle al compilador que a1..a3 ya debería estar en los registros r0..r2 cuando se ingresa la declaración asm (investigué un poco, pero no encontré ninguna pista).

+0

¿Están a1, a2, a3 un punteros? intente convertirlos a '(void *)' – osgx

+0

El atributo para un manejador de interrupción ARM es 'interrupt'. Ver http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html –

+0

@Mike: no, ese atributo aún genera una instrucción 'BLX' ... – orithena

Respuesta

0

Bueno, ahora descubrí lo que salió mal.

El concepto de JUMPing directamente a otra función es discutible en ARM Cortex, porque TriCore usa un Área de Guardar Contexto (CSA) para guardar todo el contexto de la CPU cada vez que llame a otra función. Piense en ello como una segunda pila independiente que crece con cada CALL y se contrae con cada RET. Y cada bloque CSA tiene un tamaño constante.

ARM Cortex, por otro lado, utiliza una pila estándar simple (vale, conoce una pila de sistema y una pila de hilos, pero eso no es importante aquí) y GCC simplemente guarda lo que necesita para cada función, por lo cada cuadro tiene un tamaño diferente. Simplemente saltando a otra función, por lo tanto, está fuera de la cuestión, porque la pila se dañará tan pronto como la función salta a empezar a guardar los registros no volátiles que utiliza.

Y sobre el error del enlazador con la referencia indefinida a r6 ... bueno, debería haber leído la documentación del conjunto de instrucciones con más cuidado. B es una rama incondicional a dirección inmediata, BX es la instrucción que espera la dirección de la sucursal en un registro. Fui engañado por la lista de instrucciones en el manual donde BX se describió brevemente como "Sucursal con intercambio". No quería intercambiar nada, quería un simple salto, así que no leí más.

Por lo tanto, después de cambiar B con BX en el código asm volatile, el código compilado. Pero, como se señaló anteriormente, el concepto completo no puede funcionar como se esperaba. Tal vez alguien más pueda encontrar un caso de uso para ese código, tengo que recurrir a la función clásica llamando ahora ...

1

El error de enlace se debe a que intenta utilizar la instrucción de bifurcación para saltar a un puntero. Esto genera código como b r6, que no se puede vincular porque r6 no es un símbolo. Cambie la instrucción de bifurcación al mov pc,%0, y debería obtener el salto correcto.

Como mencioné en los comentarios, los manejadores de interrupción ARM se declaran con un atributo interrupt, pero como descubriste esto no afecta cómo se llaman. Supongo que fue un truco específico de la plataforma que pasó a hacer lo correcto en TriCore.

Puede intentar declarar variables en registros específicos utilizando la sintaxis extendida de GCC, register int reg0 asm("r0") = a1; en lugar de instrucciones volátiles mov. Esto podría permitir que el compilador genere un mejor código.

Cuestiones relacionadas