簡體   English   中英

為什么 gcc -O3 處理 avx256 compare intrinsic 的方式不同於 gcc -O0 和 clang?

[英]Why does gcc -O3 handle avx256 compare intrinsic differently than gcc -O0 and clang?

我想設置兩個 integer 向量並將它們與 SIMD 進行比較,然后使用此掩碼對壓縮浮點數進行混合操作。 我制作了以下代碼:

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


int main(){
    __m256i is =  _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8);
    __m256i js =  _mm256_set1_epi32(1);               // integer bit-patterns
    __m256 mask = _mm256_cmp_ps(is,js, _CMP_EQ_OQ);   // compare as subnormal floats

    float val[8];
    memcpy(val, &mask, sizeof(val));
    printf("%f %f %f %f %f %f %f %f \n", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
}

它適用於gcc -mavx main.c以及clang -mavx main.cclang -O3 -mavx main.c

(編者注:當 cmpps 將這些非正規輸入視為0.0時,它會與-ffast-math中斷,因此所有比較都是真實的。您希望 AVX2 _mm256_cmp_epi32進行integer比較,並_mm256_castsi256_ps結果。但這與有關的問題無關gcc -O0和 clang 允許從__m256i__m256的隱式轉換)

但是,當我使用gcc -O3 -mavx main.c時,我收到以下錯誤消息:

main.c: In function ‘main’:
main.c:9:33: error: incompatible type for argument 1 of ‘_mm256_cmp_ps’
    9 |     __m256 mask = _mm256_cmp_ps(is,js, _CMP_EQ_OQ);
      |                                 ^~
      |                                 |
      |                                 __m256i {aka __vector(4) long long int}
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/include/immintrin.h:51,
                 from main.c:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/include/avxintrin.h:404:23: note: expected ‘__m256’ {aka ‘__vector(8) float’} but argument is of type ‘__m256i’ {aka ‘__vector(4) long long int’}
  404 | _mm256_cmp_ps (__m256 __X, __m256 __Y, const int __P)
      |                ~~~~~~~^~~
main.c:9:36: error: incompatible type for argument 2 of ‘_mm256_cmp_ps’
    9 |     __m256 mask = _mm256_cmp_ps(is,js, _CMP_EQ_OQ);
      |                                    ^~
      |                                    |
      |                                    __m256i {aka __vector(4) long long int}
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/include/immintrin.h:51,
                 from main.c:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/include/avxintrin.h:404:35: note: expected ‘__m256’ {aka ‘__vector(8) float’} but argument is of type ‘__m256i’ {aka ‘__vector(4) long long int’}
  404 | _mm256_cmp_ps (__m256 __X, __m256 __Y, const int __P)
      |                            ~~~~~~~^~~

我注意到兩件事。 首先,編譯器似乎將is視為__m256i {aka __vector(4) long long int}而它包含 8 個整數。 其次,編譯器的抱怨是正確的,因為英特爾內在函數指南1將 arguments 顯示為__m256 我現在很困惑為什么這段代碼在一開始就起作用了。 如果它確實是正確的,因為整數被轉換為浮點數,那么我不明白為什么它不適用於gcc -O3

我不想使用返回_mm256_cmpeq_epi32__m256i並且那里(似乎沒有)沒有接受這種掩碼的blend_ps指令。

為什么編譯器的行為不同,執行此操作的正確方法是什么?


編譯器版本

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-pkgversion='Arch Linux 9.3.0-1' --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++,d --enable-shared --enable-threads=posix --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto gdc_include_dir=/usr/include/dlang/gdc
Thread model: posix
gcc version 9.3.0 (Arch Linux 9.3.0-1) 
$ clang -v
clang version 10.0.0 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-pc-linux-gnu/8.4.0
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-pc-linux-gnu/9.3.0
Found candidate GCC installation: /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/8.4.0
Found candidate GCC installation: /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.3.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-pc-linux-gnu/8.4.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0
Found candidate GCC installation: /usr/lib64/gcc/x86_64-pc-linux-gnu/8.4.0
Found candidate GCC installation: /usr/lib64/gcc/x86_64-pc-linux-gnu/9.3.0
Selected GCC installation: /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/9.3.0
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
Found CUDA installation: /opt/cuda, version 10.1

[1] https://software.intel.com/sites/landingpage/IntrinsicsGuide/

首先,編譯器似乎將is視為__m256i {aka __vector(4) long long int}而它包含 8 個整數。

__m128i和更大的相似向量不指定存儲在其中的整數的實際大小(和數量)。 您可以使用相同的__m128i類型來存儲 16 個uint8_t或 2 個uint64_t或介於兩者之間的任何內容。 重要的部分是它用於存儲整數。 它是對__m128i和更大的相似向量的操作,它指定將向量解釋為一組給定寬度的整數。 例如, _mm_add_epi16_mm_add_epi32都接受__m128i arguments,但第一個將其解釋為 8 uint16_t的向量,第二個 - 4 uint32_t s。

其次,編譯器的抱怨是正確的,因為英特爾內在函數指南 1 將 arguments 顯示為__m256

我認為,編譯器的抱怨是正確的。 它用-O0編譯代碼似乎是一個編譯器錯誤。 在 gcc 中, __m128i和其他向量是使用__attribute__((vector_size))屬性實現的,文檔說應該使用__builtin_convertvector intrinsic 在不同類型的向量之間進行轉換。

英特爾軟件開發人員手冊第 3.1.1.10 節中__m128i和其他向量類型的原始定義沒有明確說明不同類型向量的可轉換性,盡管它確實是這樣說的:

這些 SIMD 數據類型不是基本標准 C 數據類型或 C++ 對象,因此它們只能與賦值運算符一起使用,作為 function arguments 傳遞,並從 function 調用返回。

鑒於此,我認為這些向量類型不應該是隱式可轉換的。 你當然不能依賴轉換,如果它確實編譯,會有任何特定的行為。 特別是考慮到 integer 向量未指定其元素的大小。 因此,您應該始終使用內在函數來定義所需的轉換類型,例如_mm_cvtepi32_ps / _mm_cvtepi32_pd_mm_castsi128_ps / _mm_castsi128_pd

我不想使用返回_mm256_cmpeq_epi32__m256i並且那里(似乎沒有)沒有接受這種掩碼的blend_ps指令。

_mm256_cmpeq_epi32是AVX2,AVX2中有_mm256_blendv_epi8 如果您僅限於 AVX,那么您必須對 128 位 integer 向量進行操作。

使用_mm256_cmp_ps對 integer 向量進行操作是不正確的,因為它的行為不同於 integer 比較。 特別是,如果至少一個輸入操作數與 NaN 位模式匹配(例如,使用_CMP_EQ_OQ操作數,您的比較將始終在結果向量元素中返回0 ),則有特殊規則。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM