web-dev-qa-db-ja.com

__m128i変数を出力します

私は組み込み関数を使用してコーディングすることを学ぼうとしています。以下は加算を行うコードです。

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

タイプ__m128iの変数cの値を出力するにはどうすればよいですか。

21
arunmoezhi

これらを印刷するには、この関数を使用します。

void print128_num(__m128i var)
{
    uint16_t *val = (uint16_t*) &var;
    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ビットの分割と印刷の方法です。

void print128_num(__m128i var) 
{
    int64_t *v64val = (int64_t*) &var;
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]);
}
22
askmish
  • Gcc/clang/ICC/MSVC、CおよびC++間で移植可能。
  • すべての最適化レベルで完全に安全:ポインターのエイリアスなし(他のほとんどの回答とは異なります)
  • u8、u16、u32、またはu64要素として16進数で出力します( @ AG1の回答に基づく
  • メモリ順に出力します(_mm_setr_epiXのように、最下位の要素が最初になります)。 Intelのマニュアルで使用されているのと同じ順序で印刷する場合は、配列のインデックスを逆にします。最も重要な要素は左側にあります(_mm_set_epiXなど)。関連: ベクトルレジスタを表示するための規則

__m128i*型はエイリアスを許可するように定義されているので、__m128を使用してintの配列からロードすることは安全です。 (たとえば、gccのヘッダーでは、定義に__attribute__((may_alias))が含まれます。)

は安全ではありません__m128iオブジェクトとintポインター)。ほとんどの場合はうまくいくかもしれませんが、なぜそれを危険にさらすのですか?

(uint32_t*) &my_vectorは、CおよびC++のエイリアシングルールに違反しており、期待どおりに機能することは保証されていません。ローカル配列に保存してからアクセスすると、安全が保証されます。ほとんどのコンパイラでは最適化も行われないため、movq/pextrqactualストア/リロードではなく、xmmから整数レジスタに直接取得します。例えば。

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) )を使用してください。

(組み込み関数を使用してコードを記述している場合は、最新のコンパイラバージョンを使用する必要があります。新しいコンパイラは通常、SSE/AVX組み込み関数を含む古いコンパイラよりも優れたASMを作成します。ただし、-std=gnu++03 C++でgcc-6.3を使用したい場合がありますC++ 11などの準備ができていないコードベースの03モード。)


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) を参照してください。

12
Peter Cordes

この質問に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 を参照してください

4
Antonio
#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;
}

このコードを試してください。

2
Lucien