2010-05-04 2 views
7

Je tente de construire une application qui utilise pthreads et __m128 type SSE. Selon le manuel de GCC, l'alignement de pile par défaut est de 16 octets. Afin d'utiliser __m128, l'exigence est l'alignement de 16 octets.GCC - Comment réaligner la pile?

Mon processeur cible prend en charge SSE. J'utilise un compilateur GCC qui ne prend pas en charge le réalignement de la pile d'exécution (par exemple, -mstackrealign). Je ne peux utiliser aucune autre version du compilateur GCC.

Mon application de test ressemble:

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

L'application génère une exception et sort. Après un simple débogage (printf "% p", & y), j'ai trouvé que la variable y n'est pas alignée sur 16 octets.

Ma question est: comment puis-je réaligner la pile correctement (16 octets) sans utiliser de drapeaux et d'attributs GCC (ça n'aide pas)? Dois-je utiliser GCC inline Assembler dans cette fonction de thread f()?

+2

Si vous devez utiliser une version gcc particulière, s'il vous plaît inclure la version gcc (gcc 4.3.2 i386), et l'hôte/OS cible (par exemple Debian 5.0 (lenny) Linux 2.6.26 i686). Savoir si proposer des options de gcc 4.3 par rapport à 3.4 peut faire la différence. – mctylr

Répondre

0

J'ai résolu ce problème. Voici ma solution:

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

Tout d'abord, nous augmentons la pile de 16 octets. En second lieu, nous faisons le quartet le moins significatif égal à 0x0. Nous conservons le pointeur de pile en utilisant des opérandes push/pop. Nous appelons une autre fonction, qui a toutes ses propres variables locales alignées sur 16 octets. Toutes les fonctions imbriquées auront également leurs variables locales alignées sur 16 octets.

Et ça marche!

+4

Sérieusement. METTEZ À JOUR VOTRE COMPILATEUR Ne sois pas fier de toi pour avoir mis des dispositifs de rube goldberg dans ton code. –

+6

Ce code apparaît pour sauver ESP sur la pile, puis déplace ESP ailleurs, puis pop ESP. Cela provoquera l'apparition d'une valeur aléatoire dans ESP. Cela ne cause-t-il pas un accident? Ou utilisez-vous une convention d'appel où ESP est enregistré ailleurs, peut-être dans EBP, et restauré à la fin, rendant ce POP superflu? – user9876

+0

1) Je ne peux pas mettre à jour GCC -> J'ai un environnement d'exécution spécifique et un processeur spécifique compatible x86. 2) Non, pourquoi cela peut-il causer un accident? Sauvegarde ESP, puis la restauration ne provoque aucun accident ou une valeur aléatoire. J'ai testé le code ci-dessus également sans pushl/popl et c'est aussi OK. Aucune convention d'appel et ESP ne sont enregistrés ailleurs. – psihodelia

3

Cela ne devrait pas se produire en premier lieu, mais pour contourner le problème, vous pouvez essayer:

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

Non, cela n'aide pas. Le même problème. – psihodelia

+0

Je suppose que vous faites cela sur Windows plutôt que sur un système d'exploitation approprié? Il ya quelques bonnes informations ici sur le travail autour de ce problème: http://www.sourceware.org/ml/pthreads-win32/2008/msg00056.html –

+0

Non, je travaille sur Linux – psihodelia

7

Répartir sur la pile un tableau qui est de 15 octets plus grand que sizeof(__m128) et utiliser les première adresse alignée dans ce tableau. Si vous en avez besoin de plusieurs, allouez-les dans un tableau avec une seule marge de 15 octets pour l'alignement.

Je ne me souviens pas si l'allocation d'un tableau unsigned char vous met à l'abri des optimisations strictes d'aliasing par le compilateur ou si cela ne fonctionne que dans l'autre sens.

#include <stdint.h> 

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

Vous pouvez également vérifier si la pile de threads globale est allouée avec un alignement de 16 octets. –

+0

Merci, mais qu'est-ce que ptr_t et pourquoi utilisez-vous & ~ 15? – psihodelia

+5

Malheureusement, cela force la variable à être sur la pile indépendamment des optimisations potentielles du compilateur (comme le garder dans un registre). –

1

Une autre solution consisterait à utiliser une fonction de remplissage, qui aligne d'abord la pile puis appelle f. Ainsi, au lieu d'appeler le f directement, vous appelez le pad, qui place la pile en premier, puis appelle foo avec une pile alignée.

Le code ressemblerait à ceci:

#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

Désolé de ressusciter un vieux fil ...

Pour ceux qui ont un compilateur plus récent que OP, OP mentionne une option -mstackrealign, ce qui me conduit à __attribute__((force_align_arg_pointer)). Si votre fonction est optimisée pour utiliser SSE, mais %ebp n'est pas aligné, cela fera les correctifs d'exécution si nécessaire pour vous, de manière transparente. J'ai également découvert que c'est seulement un problème sur i386. Le ABI garantit que les arguments sont alignés sur 16 octets.

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

article cool pour ceux qui voudraient en savoir plus: http://wiki.osdev.org/System_V_ABI

Questions connexes