硬件 SIMD 向量指针和相应类型之间的重新解释转换是否是未定义的行为?
在 C 中,是否允许重新解释_转换浮点数 到 __m256 并通过不同的指针访问浮点对象type?
以下代码示例说明了这一点:
#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 和 arr1 是否有未定义的行为?是否违反了严格的别名规则([basic.lval]/11)?或者,是否只有一种定义的内在方式:
__m256 hwvec2 = _mm256_load_ps(&stack_store[0 * _m256_float_step_sz]); _mm256_store_ps(&stack_store[1 * _m256_float_step_sz], hwvec2);
答案:
ISO C 没有定义 __m256,所以我们需要看看定义了什么他们在支持他们的实现上的行为。 Intel 的内在函数将像 __m256 这样的向量指针定义为允许为其他任何东西起别名,就像 ISO C 将 char 定义为允许起别名一样。 (但反之亦然:在实践中将 int* 指向 __m256i 并取消引用它是 UB 和中断。)
所以,是的,取消引用 __m256 是安全的,而不是使用 _mm256_load_ps( ) 内在的对齐负载。但特别是对于 float/double,使用内在函数通常更容易,因为它们也负责从 float 进行转换。对于整数,AVX512 加载/存储内在函数被定义为采用 void,但 AVX2 及更早版本需要像 (__m256i)&arr[i] 这样的强制转换,这是相当笨重的 API 设计,并且使用它的代码会变得混乱。
还使用 void 添加了一些非 AVX512 内在函数,例如movd/movq 加载/存储对齐和别名安全内在函数,例如 _mm_loadu_si32(void)。以前我认为英特尔假设你会使用 _mm_cvtsi32_si128 ,它需要自己安全地加载一个 int ,这意味着使用 memcpy 来避免 UB (至少在经典 ICC 和 MSVC 之外的编译器上,如果它们允许未对齐的 int* 并且不强制执行严格的
这可能是在英特尔开始考虑迁移到 LLVM 的时候ICX/ICPX / OneAPI,并意识到处理强制严格别名的编译器上的狭窄负载是多么混乱。
以上是重新解释硬件 SIMD 向量指针和相应类型之间的转换是否是 C 中未定义的行为?的详细内容。更多信息请关注PHP中文网其他相关文章!