Conteneurs SSE et C ++

Y at-il une raison évidente pour laquelle le code suivant segfaults?

#include  #include  struct point { __m128i v; point() { v = _mm_setr_epi32(0, 0, 0, 0); } }; int main(int argc, char *argv[]) { std::vector a(3); } 

Merci

Edit: J’utilise g ++ 4.5.0 sur linux / i686, je ne sais peut-être pas ce que je fais ici, mais même les segfaults suivants

 int main(int argc, char *argv[]) { point *p = new point(); } 

Je pense vraiment que ce doit être un problème d’alignement.

La chose évidente qui aurait pu se tromper serait si v n’était pas correctement aligné.

Mais il est alloué dynamicment par vector , de sorte qu’il n’est pas sujet aux problèmes de désalignement de stack.

Cependant, comme le souligne correctement phooji , une valeur “template” ou “prototype” est transmise au constructeur std::vector qui sera copiée dans tous les éléments du vecteur. C’est ce paramètre de std::vector::vector qui sera placé sur la stack et peut être mal aligné.

Certains compilateurs ont un pragma pour contrôler l’alignement de la stack dans une fonction (en gros, le compilateur gaspille l’espace supplémentaire nécessaire pour aligner correctement tous les locaux).

Selon la documentation de Microsoft, Visual C ++ 2010 doit configurer automatiquement l’alignement de stack sur 8 octets pour les types SSE, et ce, depuis Visual C ++ 2003.

Pour gcc je ne sais pas.


Sous C ++ 0x, pour new point() renvoyer un stockage non aligné est une grave non-conformité. [basic.stc.dynamic.allocation] dit (libellé du brouillon n3225):

La fonction d’allocation tente d’allouer la quantité de stockage demandée. En cas de succès, il doit renvoyer l’adresse du début d’un bloc de stockage dont la longueur en octets doit être au moins égale à la taille demandée. Le contenu de la mémoire allouée au retour de la fonction d’allocation n’est soumis à aucune contrainte. L’ordre, la contiguïté et la valeur initiale de la mémoire allouée par des appels successifs à une fonction d’allocation ne sont pas spécifiés. Le pointeur renvoyé doit être correctement aligné de sorte qu’il puisse être converti en un pointeur de tout type d’object complet avec une exigence d’alignement fondamentale (3.11), puis utilisé pour accéder à l’object ou au tableau dans la mémoire allouée (jusqu’à ce que la mémoire soit explicitement désallouée par un appel à une fonction de désallocation correspondante).

Et [basic.align] dit:

De plus, une demande d’allocation d’exécution de mémoire dynamic pour laquelle l’alignement demandé ne peut être honoré doit être traitée comme un échec d’allocation.

Pouvez-vous essayer une version plus récente de gcc où cela pourrait être corrigé?

Le constructeur de vector vous utilisez est en fait défini comme suit:

 explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() ); 

(voir par exemple, http://www.cplusplus.com/reference/stl/vector/vector/ ).

En d’autres termes, un élément est construit par défaut (c’est-à-dire, la valeur du paramètre par défaut lorsque vous appelez le constructeur) et les éléments restants sont ensuite créés en copiant le premier. Je suppose que vous avez besoin d’un constructeur de copie pour le point qui gère correctement la (non) copie des valeurs __m128i .

Mise à jour: lorsque j’essaie de générer votre code avec Visual Studio 2010 (version 10.0.30319.1), l’erreur de génération suivante s’affiche:

 error C2719: '_Val': formal parameter with __declspec(align('16')) won't be aligned c:\program files\microsoft visual studio 10.0\vc\include\vector 870 1 meh 

Cela donne à penser que Ben a raison sur l’argent et que cela pose un problème d’alignement.

Les éléments insortingnsèques de SSE doivent être alignés sur 16 octets en mémoire. Lorsque vous __m128 un __m128 sur la stack, cela ne pose aucun problème, car le compilateur les aligne automatiquement correctement. L’allocateur par défaut de std::vector<> , qui gère l’allocation de mémoire dynamic, ne produit pas d’allocations alignées.

Il est possible que la mémoire allouée par l’allocateur par défaut dans l’implémentation STL du compilateur ne soit pas alignée. Cela dépendra de la plate-forme spécifique et du fournisseur du compilateur.

L’allocateur par défaut utilise généralement l’opérateur new , ce qui ne garantit généralement pas un alignement supérieur à la taille du mot (32 bits ou 64 bits). Pour résoudre le problème, il peut être nécessaire d’implémenter un allocateur personnalisé utilisant _aligned_malloc .

De plus, un correctif simple (bien que non satisfaisant) consisterait à affecter la valeur à une variable locale __m128i , puis à copier cette variable dans la structure à l’aide d’une instruction non alignée. Exemple:

 struct point { __m128i v; point() { __m128i temp = _mm_setr_epi32(0, 0, 0, 0); _mm_storeu_si128(&v, temp); } };