2016-12-14 1 views
4

Il existe un lot de questions sur l'accès à la mémoire non allouée, ce qui est clairement un comportement non défini. Mais qu'en est-il du cas de coin suivant.Mémoire alignée partiellement allouée en lecture/écriture

Tenir compte de la struct suivante, qui est aligné sur 16 octets, mais occupe seulement 8 octets de cette:

struct alignas(16) A 
{ 
    float data[2]; // the remaining 8 bytes are unallocated 
}; 

Maintenant, nous avons accès à 16 octets de données par intrinsics de chargement/stockage alignés SSE:

__m128 test_load(const A &a) 
{ 
    return _mm_load_ps(a.data); 
} 

void test_store(A &a, __m128 v) 
{ 
    _mm_store_ps(a.data, v); 
} 

Est-ce également un comportement non défini et dois-je utiliser padding à la place? Quoi qu'il en soit, étant donné que Intel intrinsics n'est pas standard C++, accède à un bloc de mémoire partiellement alloué mais aligné (pas plus grand que la taille de l'alignement) comportement indéfini en C++ standard? Je traite à la fois le cas intrinsèque et le cas C++ standard. Je suis intéressé par les deux.

+0

La structure est donc garantie * alignée * sur 16 octets, pas * allouée * 16 octets.Vous pourriez faire une structure d'un seul caractère et l'aligner sur 1k, mais vous ne supposeriez pas que le prochain 1024 - sizeof (char) octets étaient à vous aussi. Si l'alignement était synonyme d'allocation, vous ne seriez pas capable d'aligner (16) une structure de cent flottants car une centaine de flottants est supérieure à 16 octets. –

+2

Voir aussi http://stackoverflow.com/questions/37800739/is-it-safe-to-read-past-the-end-of-a-buffer-within-the-same-page-on-x86-and -x64. C'est l'UB selon la norme ISO C++, mais je pense que l'accès en lecture seule comme celui-ci fonctionne en toute sécurité sur les implémentations qui fournissent les intrinsèques d'Intel (qui sont libres de définir le comportement supplémentaire qu'ils veulent). C'est sûrement sûr dans asm, mais le risque est que l'optimisation des compilateurs C++ qui transforment UB en code mal compilé puisse causer un problème s'ils peuvent prouver qu'il n'y a rien à lire. Il y a une discussion à ce sujet sur la question liée. –

+2

L'écriture en dehors des objets est toujours mauvaise. Ne le faites pas, même si vous remettez en place la même poubelle que vous avez déjà lue: Une paire non-atomique de chargement/stockage peut poser problème en fonction de la prochaine étape. –

Répondre

3

Voir aussi Is it safe to read past the end of a buffer within the same page on x86 and x64? La partie lecture de cette question est fondamentalement une copie de cela.

C'est UB selon la norme ISO C++, mais je pense que l'accès en lecture seule fonctionne comme ça (compiler à l'asm que vous attendez) sur les implémentations qui fournissent les intrinsèques d'Intel (qui sont libres de définir comportement supplémentaire qu'ils veulent). C'est sûrement sûr dans asm, mais le risque est que l'optimisation des compilateurs C++ qui transforment UB en code mal compilé puisse causer un problème s'ils peuvent prouver qu'il n'y a rien à lire. Il y a une discussion à ce sujet sur la question liée.


L'écriture en dehors des objets est toujours mauvais. Ne le faites pas, même si vous remettez en place la même poubelle que vous avez précédemment lue: Une paire non-atomique de chargement/stockage peut être un problème en fonction des données qui suivent votre structure.

La seule fois où cela est correct est dans un tableau où vous savez ce qui vient ensuite, et qu'il y a un remplissage inutilisé. par exemple. écrire un tableau de 3 float structs en utilisant 16B magasins se chevauchent par 4B. (Sans alignas pour le sur-alignement, donc un tableau les rassemble sans rembourrage).


A struct de 3 float s serait un exemple beaucoup mieux que 2 floats.

Pour cet exemple spécifique (de 2 flottants), vous pouvez utiliser MOVSD pour effectuer une charge 64 bits à extension nulle et MOVSD ou MOVLPS pour stocker 64 bits dans la moitié inférieure d'un __m128.

0

Une réponse de l'avocat de langue à ceci est «la question est discutable». _mm_load_ps n'est pas défini en standard et utilise une instruction ASM qui n'est pas non plus définie en standard. C++ ne gère pas cela. En ce qui concerne votre deuxième question - l'accès à une mémoire non allouée à partir de C++ est clairement un comportement non défini. Aucun objet n'a été placé dans cette mémoire, vous ne pouvez donc pas y accéder.

+0

C'est pourquoi j'aborde à la fois le cas intrinsèque ET le cas standard. Je suis intéressé par les deux. – plasmacel

+0

@plasmacel, a répondu aussi. – SergeyA

+0

Cependant, la mémoire est réservée par l'alignement. Les valeurs non allouées sont clairement indéfinies, mais êtes-vous sûr que le comportement est également indéfini? – plasmacel