2010-05-04 14 views
7

Intento crear una aplicación que utilice pthreads y __m128 tipo de SSE. De acuerdo con el manual de GCC, la alineación predeterminada de la pila es de 16 bytes. Para usar __m128, el requisito es la alineación de 16 bytes.GCC - Cómo realinear la pila?

Mi CPU de destino admite SSE. Utilizo un compilador de GCC que no admite la realineación de la pila en tiempo de ejecución (por ejemplo, -mstackrealign). No puedo usar ninguna otra versión del compilador de GCC.

Mi aplicación de prueba se parece a:

#include <xmmintrin.h> 
#include <pthread.h> 
void *f(void *x){ 
    __m128 y; 
    ... 
} 
int main(void){ 
    pthread_t p; 
    pthread_create(&p, NULL, f, NULL); 
} 

La aplicación genera una excepción y salidas. Después de una depuración simple (printf "% p", & y), encontré que la variable y no está alineada con 16 bytes.

Mi pregunta es: ¿cómo puedo realinear la pila correctamente (16 bytes) sin utilizar ninguna bandera GCC y atributos (no ayudan)? ¿Debo usar GCC en línea Ensamblador dentro de esta función de subproceso f()?

+2

Si tiene que usar una versión de gcc en particular, por favor incluya la versión de gcc (por ejemplo gcc 4.3.2 i386), y el anfitrión/sistema operativo objetivo (por ejemplo, Debian 5.0 (lenny) Linux 2.6.26 i686). Saber si sugerir opciones gcc 4.3 versus 3.4 puede marcar la diferencia. – mctylr

Respuesta

0

He resuelto este problema. Aquí está mi solución:

void another_function(){ 
    __m128 y; 
    ... 
} 
void *f(void *x){ 
asm("pushl %esp"); 
asm("subl $16,%esp"); 
asm("andl $-0x10,%esp"); 
another_function(); 
asm("popl %esp"); 
} 

En primer lugar, aumentamos la pila por 16 bytes. Segundo, hacemos un mordisco menos significativo igual a 0x0. Conservamos el puntero de la pila usando operandos push/pop. Llamamos a otra función, que tiene todas sus propias variables locales de 16 bytes alineados. Todas las funciones anidadas también tendrán sus variables locales alineadas a 16 bytes.

Y funciona!

+4

En serio. ACTUALIZA TU COMPILADOR. No se sienta orgulloso de sí mismo por poner dispositivos rube goldberg en su código. –

+6

Este código parece guardar ESP en la pila, luego mover ESP en otro lugar, luego pop ESP. Esto causará que un valor aleatorio aparezca en ESP. ¿Esto no causa un bloqueo? ¿O está utilizando una convención de llamadas donde ESP se guarda en otro lugar, tal vez en EBP, y se restaura al final, haciendo que ese POP sea superfluo? – user9876

+0

1) No puedo actualizar GCC -> Tengo un entorno de tiempo de ejecución específico y una CPU específica compatible con x86. 2) No, ¿por qué puede causar un bloqueo? Guardando ESP, luego de restaurarlo no causa ningún bloqueo o un valor aleatorio. He probado el código anterior también sin pushl/popl y también está bien. Ninguna convención de llamadas y ESP no se guardan en otro lado. – psihodelia

3

Esto no debería estar sucediendo en el primer lugar, pero para evitar el problema que puede probar:

void *f(void *x) 
{ 
    __m128 y __attribute__ ((aligned (16))); 
    ... 
} 
+0

No, no ayuda. El mismo problema. – psihodelia

+0

Supongo que está haciendo esto en Windows en lugar de un sistema operativo adecuado. Aquí hay una buena información sobre cómo solucionar este problema: http://www.sourceware.org/ml/pthreads-win32/2008/msg00056.html –

+0

No, trabajo en Linux – psihodelia

7

Asignar en la pila una matriz que es de 15 bytes más grande que sizeof(__m128), y utilizar el primera dirección alineada en esa matriz. Si necesita varios, colóquelos en una matriz con un solo margen de 15 bytes para la alineación.

No recuerdo si la asignación de una matriz unsigned char lo protege de optimizaciones estrictas de aliasing por parte del compilador o si solo funciona al revés.

#include <stdint.h> 

void *f(void *x) 
{ 
    unsigned char y[sizeof(__m128)+15]; 
    __m128 *py = (__m128*) (((uintptr_t)&y) + 15) & ~(uintptr_t)15); 
    ... 
} 
+0

También es posible que desee examinar si la pila de subprocesos general se está asignando con una alineación de 16 bytes. –

+0

Gracias, pero ¿qué es ptr_t y por qué usas & ~ 15? – psihodelia

+5

Desafortunadamente, esto obliga a la variable a estar en la pila, independientemente de las posibles optimizaciones del compilador (como mantenerlo en un registro). –

1

Otra solución sería utilizar una función de relleno, que primero alinea la pila y luego llama al f. Por lo tanto, en lugar de llamar directamente al f, llame al pad, que primero almohadilla la pila y luego llama al foo con una pila alineada.

El código se vería así:

#include <xmmintrin.h> 
#include <pthread.h> 

#define ALIGNMENT 16 

void *f(void *x) { 
    __m128 y; 
    // other stuff 
} 

void * pad(void *val) { 
    unsigned int x; // to get the current address from the stack 
    unsigned char pad[ALIGNMENT - ((unsigned int) &x) % ALIGNMENT]; 
    return f(val); 
} 

int main(void){ 
    pthread_t p; 
    pthread_create(&p, NULL, pad, NULL); 
} 
0

momento de resucitar un viejo hilo ...

Para aquellos con un compilador más reciente que la OP, OP menciona una opción -mstackrealign, que me llevan a __attribute__((force_align_arg_pointer)). Si su función está siendo optimizada para usar SSE, pero %ebp está desalineada, esto hará las correcciones de tiempo de ejecución si es necesario para usted, de forma transparente. También descubrí que esto es solo un problema en i386. El ABI x86_64 garantiza que los argumentos están alineados con 16 bytes.

__attribute__((force_align_arg_pointer)) void i_crash_when_not_aligned_to_16_bytes() { ... }

Artículo interesante para aquellos que quieran aprender más: http://wiki.osdev.org/System_V_ABI

Cuestiones relacionadas