Heim >Backend-Entwicklung >C++ >Ist die Neuinterpretation von Umwandlungen zwischen Hardware-SIMD-Vektorzeigern und entsprechenden Typen ein undefiniertes Verhalten in C?

Ist die Neuinterpretation von Umwandlungen zwischen Hardware-SIMD-Vektorzeigern und entsprechenden Typen ein undefiniertes Verhalten in C?

DDD
DDDOriginal
2024-12-27 14:38:09248Durchsuche

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

Ist das Reinterpret Casting zwischen Hardware-SIMD-Vektorzeiger und entsprechendem Typ ein undefiniertes Verhalten?

Ist es in C zulässig, einen Float neu zu interpretieren zu einem __m256 und greifen Sie über einen anderen Zeiger auf Float-Objekte zu Typ?

Das folgende Codebeispiel veranschaulicht dies:

#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);

Haben hwvec1 und arr1 undefiniertes Verhalten? Werden strenge Aliasing-Regeln verletzt ([basic.lval]/11)? Gibt es alternativ nur einen definierten intrinsischen Weg:

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

Antwort:

ISO C definiert __m256 nicht, daher müssen wir uns ansehen, was definiert ihr Verhalten gegenüber den Implementierungen, die sie unterstützen. Die intrinsischen Funktionen von Intel definieren Vektorzeiger wie __m256 als erlaubt, alles andere zu aliasen, genauso wie ISO C char als erlaubt definiert, Alias ​​zu verwenden. (Aber nicht umgekehrt: Es ist UB und bricht in der Praxis, einen int* auf einen __m256i zu verweisen und ihn zu dereferenzieren.)

Also ja, es ist sicher, einen __m256 zu dereferenzieren, anstatt einen _mm256_load_ps( zu verwenden. ) ausgerichtete Last intrinsisch. Aber besonders für Float/Double ist es oft einfacher, die Intrinsics zu verwenden, da sie auch das Casting aus Float übernehmen. Für Ganzzahlen sind die AVX512-Lade-/Speicherfunktionen so definiert, dass sie void annehmen, aber AVX2 und früher benötigen eine Umwandlung wie (__m256i)&arr[i], was ein ziemlich umständliches API-Design ist und den Code, der es verwendet, überfüllt.

Ein paar Nicht-AVX512-Intrinsics wurden auch mit voidlike hinzugefügt movd/movq lädt/speichert Ausrichtung und Aliasing sicherer intrinsischer Elemente wie _mm_loadu_si32(void). Ich denke, Intel ging vorher davon aus, dass Sie _mm_cvtsi32_si128 verwenden würden, was erforderte, dass Sie ein int sicher selbst laden mussten, was bedeutete, memcpy zu verwenden, um UB zu vermeiden (zumindest auf anderen Compilern als dem klassischen ICC und MSVC, wenn sie nicht ausgerichtetes int* zulassen und keine strenge Durchsetzung erzwingen). Aliasing).

Dies könnte ungefähr zu der Zeit gewesen sein, als Intel begann, über eine Migration zu LLVM nachzudenken ICX/ICPX/OneAPI und die Erkenntnis, wie viel Chaos es war, mit engen Lasten auf Compilern umzugehen, die striktes Aliasing erzwingen.

Das obige ist der detaillierte Inhalt vonIst die Neuinterpretation von Umwandlungen zwischen Hardware-SIMD-Vektorzeigern und entsprechenden Typen ein undefiniertes Verhalten in C?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn