打印 __m128i 变量

问题描述 投票:0回答:4

我正在尝试学习使用内在函数进行编码,下面是一个进行加法的代码

compiler used: icc

#include<stdio.h>
#include<emmintrin.h>
int main()
{
        __m128i a = _mm_set_epi32(1,2,3,4);
        __m128i b = _mm_set_epi32(1,2,3,4);
        __m128i c;
        c = _mm_add_epi32(a,b);
        printf("%d\n",c[2]);
        return 0;
}

我收到以下错误:

test.c(9): error: expression must have pointer-to-object type
        printf("%d\n",c[2]);

如何打印类型为

c
 的变量 
__m128i

中的值
c assembly sse simd intrinsics
4个回答
30
投票

使用此功能打印它们:

#include <stdint.h>
#include <string.h>

void print128_num(__m128i var)
{
    uint16_t val[8];
    memcpy(val, &var, sizeof(val));
    printf("Numerical: %i %i %i %i %i %i %i %i \n", 
           val[0], val[1], val[2], val[3], val[4], val[5], 
           val[6], val[7]);
}

在打印之前将 128 位拆分为 16 位(或 32 位)。

如果您有 64 位支持,这是一种 64 位分割和打印的方式:

#include <inttypes.h>

void print128_num(__m128i var) 
{
    int64_t v64val[2];
    memcpy(v64val, &var, sizeof(v64val));
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]);
}

注意:

&var
直接转换为
int*
uint16_t*
也可以使用 MSVC,但这违反了严格的别名,并且是未定义的行为。使用
memcpy
是执行相同操作的标准兼容方法,并且通过最小的优化,编译器将生成完全相同的二进制代码。


22
投票
  • 可跨 gcc/clang/ICC/MSVC、C 和 C++ 移植。
  • 所有优化级别都完全安全:没有严格别名违规 UB
  • 以十六进制打印为 u8、u16、u32 或 u64 元素(基于@AG1 的答案),
    或任何你想要的格式字符串。
  • 按内存顺序打印(首先是最不重要的元素,如
    _mm_setr_epiX
    )。如果您希望按照英特尔手册使用的相同顺序打印,请反转数组索引,其中最重要的元素位于左侧(如
    _mm_set_epiX
    )。相关:矢量寄存器显示约定

使用

__m128i*
int
数组加载是安全的,因为
__m128
类型被定义为允许别名,就像 ISO C
unsigned char*
一样。 (例如,在 gcc 的标头中,定义包括
__attribute__((may_alias))
。)

相反的安全(将

int*
指向
__m128i
对象的一部分)。 MSVC 保证这是安全的,但 GCC/clang 则不然。 (默认情况下
-fstrict-aliasing
处于打开状态)。有时它可以与 GCC/clang 一起使用,但为什么要冒险呢?有时甚至会干扰优化;请参阅此问答。另请参阅 硬件 SIMD 向量指针和相应类型之间的“reinterpret_cast”是否是未定义的行为?

请参阅 GCC AVX __m256i 转换为 int 数组导致错误值,了解 GCC 破坏代码的真实示例,该代码将

int*
指向
__m256i


(uint32_t*) &my_vector
违反了 C 和 C++ 别名规则,并且不能保证按您期望的方式工作。存储到本地数组然后访问它是保证安全的。它甚至对大多数编译器进行了优化,因此您可以直接从 xmm 到整数寄存器获得
movq
/
pextrq
,而不是例如 实际 存储/重新加载。

Godbolt 编译器资源管理器上的源代码 + asm 输出:证明它可以使用 MSVC 等进行编译。

#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>

#ifndef __cplusplus
#include <stdalign.h>   // C11 defines _Alignas().  This header defines alignas()
#endif

void p128_hex_u8(__m128i in) {
    alignas(16) uint8_t v[16];
    _mm_store_si128((__m128i*)v, in);
    printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x\n",
           v[0], v[1],  v[2],  v[3],  v[4],  v[5],  v[6],  v[7],
           v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
}

void p128_hex_u16(__m128i in) {
    alignas(16) uint16_t v[8];
    _mm_store_si128((__m128i*)v, in);
    printf("v8_u16: %x %x %x %x,  %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
}

void p128_hex_u32(__m128i in) {
    alignas(16) uint32_t v[4];
    _mm_store_si128((__m128i*)v, in);
    printf("v4_u32: %x %x %x %x\n", v[0], v[1], v[2], v[3]);
}

void p128_hex_u64(__m128i in) {
    alignas(16) unsigned long long v[2];  // uint64_t might give format-string warnings with %llx; it's just long in some ABIs
    _mm_store_si128((__m128i*)v, in);
    printf("v2_u64: %llx %llx\n", v[0], v[1]);
}

如果您需要移植到 C99 或 C++03 或更早版本(即没有 C11 / C++11),请删除

alignas()
并使用
storeu
而不是
store
。或者使用
__attribute__((aligned(16)))
__declspec( align(16) )
代替。

(如果您正在使用内在函数编写代码,则应该使用最新的编译器版本。较新的编译器通常比旧编译器生成更好的 asm,包括 SSE/AVX 内在函数。但也许您想使用 gcc-6.3 和

-std=gnu++03
C++03 模式适用于尚未准备好支持 C++11 或其他内容的代码库。)


调用所有 4 个函数的输出示例

// source used:
__m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7,
                            8, 9, 10, 11, 12, 13, 14, 15, 16);

// output:

v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09
v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d
v8_u16: 0x201 0x403 0x605 0x807  | 0xa09 0xc0b 0xe0d 0x100f
v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10

如果您想用前导零填充以获得一致的输出宽度,请调整格式字符串。请参阅

printf(3)


5
投票

我知道这个问题被标记为 C,但在寻找同一问题的 C++ 解决方案时,它也是最好的搜索结果。

所以,这可能是一个 C++ 实现:

#include <string>
#include <cstring>
#include <sstream>

#if defined(__SSE2__)
template <typename T>
std::string __m128i_toString(const __m128i var) {
    std::stringstream sstr;
    T values[16/sizeof(T)];
    std::memcpy(values,&var,sizeof(values)); //See discussion below
    if (sizeof(T) == 1) {
        for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible
            sstr << (int) values[i] << " ";
        }
    } else {
        for (unsigned int i = 0; i < sizeof(__m128i) / sizeof(T); i++) { //C++11: Range for also possible
            sstr << values[i] << " ";
        }
    }
    return sstr.str();
}
#endif

用途:

#include <iostream>
[..]
__m128i x
[..]
std::cout << __m128i_toString<uint8_t>(x) << std::endl;
std::cout << __m128i_toString<uint16_t>(x) << std::endl;
std::cout << __m128i_toString<uint32_t>(x) << std::endl;
std::cout << __m128i_toString<uint64_t>(x) << std::endl;

结果:

141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0
29325 0 0 0 26775 0 0 0
29325 0 26775 0
29325 26775

注意:有一种简单的方法可以避免

if (size(T)==1)
,请参阅 https://stackoverflow.com/a/28414758/2436175


2
投票
#include<stdio.h>
#include<emmintrin.h>
int main()
{
    __m128i a = _mm_set_epi32(1,2,3,4);
    __m128i b = _mm_set_epi32(1,2,3,4);
    __m128i c;

    const int32_t* q; 
    //add a pointer 
    c = _mm_add_epi32(a,b);

    q = (const int32_t*) &c;
    printf("%d\n",q[2]);
    //printf("%d\n",c[2]);
    return 0;
}

试试这个代码。

© www.soinside.com 2019 - 2024. All rights reserved.