2010-05-04 21 views
7

Provo a creare un'applicazione che utilizza pthreads e __m128 SSE type. Secondo il manuale GCC, l'allineamento di stack predefinito è 16 byte. Per utilizzare __m128, il requisito è l'allineamento a 16 byte.GCC - Come riallineare lo stack?

La mia CPU di destinazione supporta SSE. Uso un compilatore GCC che non supporta il riallineamento dello stack di runtime (ad esempio -mstackrealign). Non posso usare nessuna altra versione del compilatore GCC.

La mia applicazione di test si presenta come:

#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'applicazione genera un'eccezione ed esce. Dopo un semplice debug (printf "% p", & y), ho trovato che la variabile y non è allineata a 16 byte.

La mia domanda è: come posso riallineare correttamente lo stack (16 byte) senza utilizzare alcun flag GCC e attributi (non aiutano)? Dovrei usare GCC inline Assembler all'interno di questa funzione thread f()?

+2

Se è necessario utilizzare una particolare versione di gcc, si prega di includere la versione di gcc (ad esempio, gcc 4.3.2 i386), e host/SO di destinazione (es. Debian 5.0 (lenny) Linux 2.6.26 i686). Sapere se suggerire le opzioni di gcc 4.3 rispetto a 3.4 può fare la differenza. – mctylr

risposta

0

Ho risolto questo problema. Ecco la mia soluzione:

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

In primo luogo, aumentiamo lo stack da 16 byte. In secondo luogo, rendiamo il nibble meno significativo uguale a 0x0. Conserviamo il puntatore dello stack usando gli operandi push/pop. Chiamiamo un'altra funzione, che ha tutte le sue variabili locali allineate a 16 byte. Tutte le funzioni annidate avranno anche le loro variabili locali allineate a 16 byte.

E funziona!

+4

Seriamente. AGGIORNA IL TUO COMPILATORE. Non essere orgoglioso di te stesso per aver inserito i dispositivi rube goldberg nel tuo codice. –

+6

Questo codice sembra salvare ESP nello stack, quindi spostare ESP da qualche altra parte, quindi eseguire il pop ESP. Ciò causerà l'estrazione di un valore casuale in ESP. Questo non causa un crash? O stai usando una convenzione di chiamata in cui l'ESP viene salvato da qualche altra parte, forse in EBP, e ripristinato alla fine, rendendo questo POP superfluo? – user9876

+0

1) Non riesco ad aggiornare GCC -> Ho un ambiente di runtime specifico e una CPU compatibile x86 specifica. 2) No, perché può causare un arresto anomalo? Salvando ESP, il ripristino non causa alcun arresto anomalo o un valore casuale. Ho testato il codice sopra anche senza pushl/popl ed è anche Ok. Nessuna convenzione di chiamata e ESP non vengono salvate da qualche altra parte. – psihodelia

3

Questo non dovrebbe accadere in primo luogo, ma per aggirare il problema è possibile provare:

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

No, non aiuta. Lo stesso problema. – psihodelia

+0

La mia ipotesi è che lo stai facendo su Windows piuttosto che su un sistema operativo adeguato? Ci sono alcune buone informazioni qui su come aggirare questo problema: http://www.sourceware.org/ml/pthreads-win32/2008/msg00056.html –

+0

No, io lavoro su Linux – psihodelia

7

Assegnare sullo stack una matrice che è di 15-byte più grande di sizeof(__m128), e utilizzare il primo indirizzo allineato in quell'array. Se sono necessari diversi, allocarli in un array con un singolo margine di 15 byte per l'allineamento.

Non ricordo se l'assegnazione di un array unsigned char ti rende al sicuro da ottimizzazioni di aliasing da parte del compilatore o se funziona solo il contrario.

#include <stdint.h> 

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

Si potrebbe anche voler esaminare se lo stack di thread generale viene allocato con un allineamento a 16 byte. –

+0

Grazie, ma cos'è ptr_t e perché usi & ~ 15? – psihodelia

+5

Sfortunatamente questo impone che la variabile sia nello stack indipendentemente dalle potenziali ottimizzazioni del compilatore (come tenerla in un registro). –

1

Un'altra soluzione potrebbe essere, per utilizzare una funzione di riempimento, che prima allinea lo stack e quindi chiama f. Quindi, invece di chiamare direttamente lo f, si chiama pad, che blocca prima lo stack e poi chiama foo con uno stack allineato.

Il codice sarebbe simile a questa:

#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

dispiace di far risorgere un vecchio filo ...

Per quelli con un compilatore più recente di OP, OP menzioni un'opzione -mstackrealign, che mi portano a __attribute__((force_align_arg_pointer)). Se la tua funzione è ottimizzata per usare SSE, ma %ebp è disallineato, questo eseguirà le correzioni di runtime se necessario, in modo trasparente. Ho anche scoperto che questo è solo un problema su i386. L'ABI x86_64 garantisce che gli argomenti siano allineati a 16 byte.

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

Fantastico articolo per chi volesse saperne di più: http://wiki.osdev.org/System_V_ABI