Maison >développement back-end >C++ >La réinterprétation des conversions entre les pointeurs vectoriels matériels SIMD et les types correspondants est-elle un comportement non défini en C ?

La réinterprétation des conversions entre les pointeurs vectoriels matériels SIMD et les types correspondants est-elle un comportement non défini en C ?

DDD
DDDoriginal
2024-12-27 14:38:09248parcourir

Is Reinterpreting Casts Between Hardware SIMD Vector Pointers and Corresponding Types Undefined Behavior in C  ?

La réinterprétation de la diffusion entre le pointeur vectoriel SIMD matériel et le type correspondant est-elle un comportement non défini ?

En C, est-il permis de réinterpréter_cast un float à un __m256 et accédez aux objets flottants via un pointeur différent type ?

L'exemple de code suivant illustre ceci :

#include <immintrin.h>

constexpr size_t _m256_float_step_sz = sizeof(__m256) / sizeof(float);
alignas(__m256) float stack_store[100 * _m256_float_step_sz ]{};
__m256& hwvec1 = *reinterpret_cast<__m256*&>(&stack_store[0 * _m256_float_step_sz]);

using arr_t = float[_m256_float_step_sz];
arr_t& arr1 = *reinterpret_cast<float(*)[_m256_float_step_sz]&>(&hwvec1);

Hwvec1 et arr1 ont-ils des comportements non définis ? Les règles strictes d'alias sont-elles violées ([basic.lval]/11) ? Alternativement, existe-t-il une seule manière intrinsèque définie :

__m256 hwvec2 = _mm256_load_ps(&stack_store[0 * _m256_float_step_sz]);
_mm256_store_ps(&stack_store[1 * _m256_float_step_sz], hwvec2);

Réponse :

ISO C ne définit pas __m256, nous devons donc examiner ce qui définit leur comportement sur les implémentations qui les supportent. Les valeurs intrinsèques d'Intel définissent les pointeurs vectoriels comme __m256 comme étant autorisés à créer un alias, de la même manière que l'ISO C définit char comme étant autorisé à créer un alias. (Mais pas l'inverse : c'est UB et c'est une pratique courante de pointer un int* sur un __m256i et de le déréférencer.)

Alors oui, il est prudent de déréférencer un __m256 au lieu d'utiliser un _mm256_load_ps( ) charge alignée intrinsèque. Mais surtout pour float/double, il est souvent plus facile d'utiliser les intrinsèques car ils s'occupent également du casting depuis float. Pour les entiers, les intrinsèques de chargement/stockage AVX512 sont définis comme prenant void mais AVX2 et les versions antérieures ont besoin d'un cast comme (__m256i)&arr[i] qui est une conception d'API assez maladroite et encombre le code qui l'utilise.

Quelques éléments intrinsèques non-AVX512 ont également été ajoutés en utilisant void comme movd/movq charge/stocke l'alignement et l'alias des intrinsèques sûrs tels que _mm_loadu_si32(void). Auparavant, je pense qu'Intel supposait que vous utiliseriez _mm_cvtsi32_si128, ce qui nécessitait de charger vous-même un int en toute sécurité, ce qui signifiait utiliser memcpy pour éviter UB (au moins sur les compilateurs autres que ICC et MSVC classiques, s'ils autorisent des int* non alignés et n'appliquent pas de règles strictes. aliasing).

Cela aurait pu se produire à l'époque où Intel a commencé à envisager de migrer vers LLVM pour ICX/ICPX/OneAPI, et réaliser à quel point il était compliqué de gérer des charges étroites sur les compilateurs qui appliquent un alias strict.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn