zoukankan      html  css  js  c++  java
  • [C] zintrin.h : 智能引入intrinsic函数。支持VC、GCC,兼容Windows、Linux、Mac OS X

    博客来源:http://blog.csdn.net/zyl910/article/details/8100744

    现在很多编译器支持intrinsic函数,这给编写SSE等SIMD代码带来了方便。但是各个编译器略有差异,于是我编写了zintrin.h,智能引入intrinsic函数。


    一、各种编译器的区别

    1.1 Visual C++(Windows)

      最早支持intrinsic函数的VC编译器是VC 6.0。它在装上Visual Studio 6.0 Service Pack 5、Visual C++ 6.0 Processor Pack这两个补丁后,便提供了mmintrin.h、mm3dnow.h、xmmintrin.h、emmintrin.h,用于支持MMX、3DNow!、SSE、SSE2的intrinsic函数。
      从VC2005开始,提供了intrin.h,用于引入所有的intrinsic函数。
      详见——
    http://www.cnblogs.com/zyl910/archive/2012/02/28/vs_intrin_table.html
    Intrinsics头文件与SIMD指令集、Visual Studio版本对应表

      如果希望得知当前编译环境是否支持某种intrinsic函数,只能利用_MSC_VER判断VC的版本 来间接确认。
      而且实际过程中会发现一些兼容性小问题,例如——VC不支持x64下的MMX指令、VC2008之前没有_mm_cvtss_f32 等。


    1.2 GCC(Linux下的GCC、Windows下的MinGW)

      gcc也支持intrinsic函数。例如在Fedora 17中,“/usr/lib/gcc/i686-redhat-linux/4.7.0/include/”目录下有Intrinsics头文件。而对于Windows中的MinGW,Intrinsics头文件是在MinGW的“libgccmingw324.6.2include”子目录中。
      详见——
    http://www.cnblogs.com/zyl910/archive/2012/08/27/intrin_table_gcc.html
    GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表

      gcc允许通过命令行参数来控制是否打开某种指令集的支持,例如“-mmmx”用于打开MMX支持。可在终端中执行“gcc --target-help”,得到详细的参数列表。

      当通过命令行参数打开指令集支持后,gcc会自动定义对应的预处理宏。例如用“-mmmx”打开MMX支持后,gcc会自动定义“__MMX__”这个预定义宏。这一类的预定义宏有——
    __MMX__
    __3dNOW__
    __SSE__
    __SSE2__
    __SSE3__
    __SSSE3__
    __SSE4_1__
    __SSE4_2__
    __SSE4A__
    __AES__
    __PCLMUL__
    __AVX__
    __AVX2__
    __F16C__
    __FMA__
    __FMA4__
    __XOP__
    __LWP__
    __RDRND__
    __FSGSBASE__
    __LZCNT__
    __POPCNT__
    __BMI__
    __BMI2__
    __TBM__

      gcc用于引入所有x86平台intrinsic函数的头文件是“x86intrin.h”,它会根据那些指令集预定义宏来引入相关的intrinsic函数。例如有“__MMX__”宏时,x86intrin.h会引入MMX的intrinsic函数。


    1.3 Mac OS X 中的 llvm-gcc

      我在Mac OS X系统中找了很久,貌似它不支持intrinsic函数。详细版本是——
    操作系统:Mac OS X Lion 10.7.4(11E53)
    编程工具:Xcode 4.4.1(1448),并装好了它的“Command Line Tools”。

      __llvm__这个预定义宏可用来判断是不是llvm-gcc。


    二、设计

    2.1 思路

      不同的编译器引入intrinsic函数的办法——
    对于VS2005之前的版本,只能手动逐个逐个的包含emmintrin.h、mm3dnow.h;
    对于VS2005之后的版本,可以利用intrin.h引入所有intrinsic函数;
    对于GCC,首先应该判断__llvm__宏来排除llvm-gcc,然后利用x86intrin.h引入所有intrinsic函数。

      这样做太麻烦了,我想编写一个头文件智能引入intrinsic函数。这就是zintrin.h。

      其次——
    VC中,没有直接判断是否支持某种intrinsic函数的办法,只能利用_MSC_VER判断VC的版本 来间接确认。而且还有x64环境下不支持MMX等特殊情况。
    对于GCC,虽然可以利用__MMX__等宏判断是否打开了指令集支持,但这并不代表支持intrinsic函数。例如Mac OS X 中的 llvm-gcc,默认打开了__MMX__、__SSE__、__SSE2__,但它不支持intrinsic函数。

      于是我想,如果有一种统一的方式判断当前编译环境是否支持某种intrinsic函数的办法就好了。


    2.2 功能说明

      功能——
    1. 引入了编译器支持的所有intrinsic函数.
    2. 提供了 INTRIN_MMX 等一系列宏用于判断当前编译环境是否支持该intrin函数.
    3. 兼容性补丁. 例如 _mm_cvtss_f32 等.


      用于判断当前编译环境是否支持该intrin函数的宏:
    INTRIN_MMX
    INTRIN_3dNOW
    INTRIN_SSE
    INTRIN_SSE2
    INTRIN_SSE3
    INTRIN_SSSE3
    INTRIN_SSE4_1
    INTRIN_SSE4_2
    INTRIN_SSE4A
    INTRIN_AES
    INTRIN_PCLMUL
    INTRIN_AVX
    INTRIN_AVX2
    INTRIN_F16C
    INTRIN_FMA
    INTRIN_FMA4
    INTRIN_XOP
    INTRIN_LWP
    INTRIN_RDRND
    INTRIN_FSGSBASE
    INTRIN_LZCNT
    INTRIN_POPCNT
    INTRIN_BMI
    INTRIN_BMI2
    INTRIN_TBM


    三、源码

    3.1 zintrin.h

      全部代码——

    [cpp] view plaincopy
     
    1. #ifndef __ZINTRIN_H_INCLUDED  
    2. #define __ZINTRIN_H_INCLUDED  
    3.   
    4. // 根据不同的编译器做不同的处理.  
    5. #if defined(__GNUC__)   // GCC  
    6.     #if (defined(__i386__) || defined(__x86_64__) ) && !defined(__llvm__)   // Mac OS X 的 llvm 不支持 intrin 函数.  
    7.         // header files  
    8.         #include <x86intrin.h>  
    9.         #include <cpuid.h>  
    10.   
    11.         // macros  
    12.         #ifdef __MMX__  
    13.             #define INTRIN_MMX  1  
    14.         #endif  
    15.         #ifdef __3dNOW__  
    16.             #define INTRIN_3dNOW    1  
    17.         #endif  
    18.         #ifdef __SSE__  
    19.             #define INTRIN_SSE  1  
    20.         #endif  
    21.         #ifdef __SSE2__  
    22.             #define INTRIN_SSE2 1  
    23.         #endif  
    24.         #ifdef __SSE3__  
    25.             #define INTRIN_SSE3 1  
    26.         #endif  
    27.         #ifdef __SSSE3__  
    28.             #define INTRIN_SSSE3    1  
    29.         #endif  
    30.         #ifdef __SSE4_1__  
    31.             #define INTRIN_SSE4_1   1  
    32.         #endif  
    33.         #ifdef __SSE4_2__  
    34.             #define INTRIN_SSE4_2   1  
    35.         #endif  
    36.         #ifdef __SSE4A__  
    37.             #define INTRIN_SSE4A    1  
    38.         #endif  
    39.         #ifdef __AES__  
    40.             #define INTRIN_AES  1  
    41.         #endif  
    42.         #ifdef __PCLMUL__  
    43.             #define INTRIN_PCLMUL   1  
    44.         #endif  
    45.         #ifdef __AVX__  
    46.             #define INTRIN_AVX  1  
    47.         #endif  
    48.         #ifdef __AVX2__  
    49.             #define INTRIN_AVX2 1  
    50.         #endif  
    51.         #ifdef __F16C__  
    52.             #define INTRIN_F16C 1  
    53.         #endif  
    54.         #ifdef __FMA__  
    55.             #define INTRIN_FMA  1  
    56.         #endif  
    57.         #ifdef __FMA4__  
    58.             #define INTRIN_FMA4 1  
    59.         #endif  
    60.         #ifdef __XOP__  
    61.             #define INTRIN_XOP  1  
    62.         #endif  
    63.         #ifdef __LWP__  
    64.             #define INTRIN_LWP  1  
    65.         #endif  
    66.         #ifdef __RDRND__  
    67.             #define INTRIN_RDRND    1  
    68.         #endif  
    69.         #ifdef __FSGSBASE__  
    70.             #define INTRIN_FSGSBASE 1  
    71.         #endif  
    72.         #ifdef __LZCNT__  
    73.             #define INTRIN_LZCNT    1  
    74.         #endif  
    75.         #ifdef __POPCNT__  
    76.             #define INTRIN_POPCNT   1  
    77.         #endif  
    78.         #ifdef __BMI__  
    79.             #define INTRIN_BMI  1  
    80.         #endif  
    81.         #ifdef __BMI2__  
    82.             #define INTRIN_BMI2 1  
    83.         #endif  
    84.         #ifdef __TBM__  
    85.             #define INTRIN_TBM  1  
    86.         #endif  
    87.   
    88.     #endif  // #if !defined(__llvm__)  
    89.   
    90. #elif defined(_MSC_VER) // MSVC  
    91.     // header files  
    92.     #if _MSC_VER >=1400  // VC2005  
    93.         #include <intrin.h>  
    94.     #elif _MSC_VER >=1200    // VC6  
    95.         #if (defined(_M_IX86) || defined(_M_X64))  
    96.             #include <emmintrin.h>    // MMX, SSE, SSE2  
    97.             #include <mm3dnow.h>  // 3DNow!  
    98.         #endif  
    99.     #endif  // #if _MSC_VER >=1400  
    100.     #include <malloc.h>   // _mm_malloc, _mm_free.  
    101.   
    102.     // macros  
    103.     #if (defined(_M_IX86) || defined(_M_X64))  
    104.         #if _MSC_VER >=1200  // VC6  
    105.             #if defined(_M_X64) && !defined(__INTEL_COMPILER)  
    106.                 // VC编译器不支持64位下的MMX.  
    107.             #else  
    108.                 #define INTRIN_MMX  1   // mmintrin.h  
    109.                 #define INTRIN_3dNOW    1   // mm3dnow.h  
    110.             #endif  
    111.             #define INTRIN_SSE  1   // xmmintrin.h  
    112.             #define INTRIN_SSE2 1   // emmintrin.h  
    113.         #endif  
    114.         #if _MSC_VER >=1300  // VC2003  
    115.         #endif  
    116.         #if _MSC_VER >=1400  // VC2005  
    117.         #endif  
    118.         #if _MSC_VER >=1500  // VC2008  
    119.             #define INTRIN_SSE3 1   // pmmintrin.h  
    120.             #define INTRIN_SSSE3    1   // tmmintrin.h  
    121.             #define INTRIN_SSE4_1   1   // smmintrin.h  
    122.             #define INTRIN_SSE4_2   1   // nmmintrin.h  
    123.             #define INTRIN_SSE4A    1   // intrin.h  
    124.             #define INTRIN_LZCNT    1   // intrin.h  
    125.             #define INTRIN_POPCNT   1   // nmmintrin.h  
    126.         #endif  
    127.         #if _MSC_VER >=1600  // VC2010  
    128.             #define INTRIN_AES  1   // wmmintrin.h  
    129.             #define INTRIN_PCLMUL   1   // wmmintrin.h  
    130.             #define INTRIN_AVX  1   // immintrin.h  
    131.             #define INTRIN_FMA4 1   // ammintrin.h  
    132.             #define INTRIN_XOP  1   // ammintrin.h  
    133.             #define INTRIN_LWP  1   // ammintrin.h  
    134.         #endif  
    135.         #if _MSC_VER >=1700  // VC2012  
    136.             #define INTRIN_AVX2 0   //TODO:待查证. 先设为0.  
    137.             #define INTRIN_FMA  0  
    138.             #define INTRIN_F16C 0  
    139.             #define INTRIN_RDRND    0  
    140.             #define INTRIN_FSGSBASE 0  
    141.             #define INTRIN_BMI  0  
    142.             #define INTRIN_BMI2 0  
    143.             #define INTRIN_TBM  0  
    144.         #endif  
    145.     #endif  
    146.     //TODO:待查证 VS配合intel C编译器时intrin函数的支持性.  
    147.   
    148.     // VC2008之前没有_mm_cvtss_f32  
    149.     #if _MSC_VER <1500   // VC2008  
    150.         // float _mm_cvtss_f32(__m128 _A);  
    151.         #ifndef _mm_cvtss_f32  
    152.             #define _mm_cvtss_f32(__m128_A) ( *(float*)(void*)&(__m128_A) )  
    153.         #endif  
    154.     #endif  
    155.   
    156. #else  
    157. //#error Only supports GCC or MSVC.  
    158. #endif  // #if defined(__GNUC__)  
    159.   
    160. #endif  // #ifndef __ZINTRIN_H_INCLUDED  


    3.2 testzintrin.c

      全部代码——

    [cpp] view plaincopy
     
    1. #include <stdio.h>  
    2.   
    3. #include "zintrin.h"  
    4.   
    5. #define PT_MAKE_STR(x)  { #x, PT_MAKE_STR_ESC(x) }  
    6. #define PT_MAKE_STR_ESC(x)  #x  
    7.   
    8. typedef struct tagMACRO_T  
    9. {  
    10.     const char *name;  
    11.     const char *value;  
    12. } MACRO_T;  
    13.   
    14. /* Intrinsics */  
    15. const MACRO_T g_intrins[] =  
    16. {  
    17.     {"[Intrinsics]", ""},  
    18.   
    19. #ifdef INTRIN_MMX  
    20.     PT_MAKE_STR(INTRIN_MMX),  
    21. #endif  
    22.   
    23. #ifdef INTRIN_3dNOW  
    24.     PT_MAKE_STR(INTRIN_3dNOW),  
    25. #endif  
    26.   
    27. #ifdef INTRIN_SSE  
    28.     PT_MAKE_STR(INTRIN_SSE),  
    29. #endif  
    30.   
    31. #ifdef INTRIN_SSE2  
    32.     PT_MAKE_STR(INTRIN_SSE2),  
    33. #endif  
    34.   
    35. #ifdef INTRIN_SSE3  
    36.     PT_MAKE_STR(INTRIN_SSE3),  
    37. #endif  
    38.   
    39. #ifdef INTRIN_SSSE3  
    40.     PT_MAKE_STR(INTRIN_SSSE3),  
    41. #endif  
    42.   
    43. #ifdef INTRIN_SSE4_1  
    44.     PT_MAKE_STR(INTRIN_SSE4_1),  
    45. #endif  
    46.   
    47. #ifdef INTRIN_SSE4_2  
    48.     PT_MAKE_STR(INTRIN_SSE4_2),  
    49. #endif  
    50.   
    51. #ifdef INTRIN_SSE4A  
    52.     PT_MAKE_STR(INTRIN_SSE4A),  
    53. #endif  
    54.   
    55. #ifdef INTRIN_AES  
    56.     PT_MAKE_STR(INTRIN_AES),  
    57. #endif  
    58.   
    59. #ifdef INTRIN_PCLMUL  
    60.     PT_MAKE_STR(INTRIN_PCLMUL),  
    61. #endif  
    62.   
    63. #ifdef INTRIN_AVX  
    64.     PT_MAKE_STR(INTRIN_AVX),  
    65. #endif  
    66.   
    67. #ifdef INTRIN_AVX2  
    68.     PT_MAKE_STR(INTRIN_AVX2),  
    69. #endif  
    70.   
    71. #ifdef INTRIN_F16C  
    72.     PT_MAKE_STR(INTRIN_F16C),  
    73. #endif  
    74.   
    75. #ifdef INTRIN_FMA  
    76.     PT_MAKE_STR(INTRIN_FMA),  
    77. #endif  
    78.   
    79. #ifdef INTRIN_FMA4  
    80.     PT_MAKE_STR(INTRIN_FMA4),  
    81. #endif  
    82.   
    83. #ifdef INTRIN_XOP  
    84.     PT_MAKE_STR(INTRIN_XOP),  
    85. #endif  
    86.   
    87. #ifdef INTRIN_LWP  
    88.     PT_MAKE_STR(INTRIN_LWP),  
    89. #endif  
    90.   
    91. #ifdef INTRIN_RDRND  
    92.     PT_MAKE_STR(INTRIN_RDRND),  
    93. #endif  
    94.   
    95. #ifdef INTRIN_FSGSBASE  
    96.     PT_MAKE_STR(INTRIN_FSGSBASE),  
    97. #endif  
    98.   
    99. #ifdef INTRIN_LZCNT  
    100.     PT_MAKE_STR(INTRIN_LZCNT),  
    101. #endif  
    102.   
    103. #ifdef INTRIN_POPCNT  
    104.     PT_MAKE_STR(INTRIN_POPCNT),  
    105. #endif  
    106.   
    107. #ifdef INTRIN_BMI  
    108.     PT_MAKE_STR(INTRIN_BMI),  
    109. #endif  
    110.   
    111. #ifdef INTRIN_BMI2  
    112.     PT_MAKE_STR(INTRIN_BMI2),  
    113. #endif  
    114.   
    115. #ifdef INTRIN_TBM  
    116.     PT_MAKE_STR(INTRIN_TBM),  
    117. #endif  
    118.   
    119. };  
    120.   
    121.   
    122. // 获取程序位数(被编译为多少位的代码)  
    123. int GetProgramBits(void)  
    124. {  
    125.     return sizeof(int*) * 8;  
    126. }  
    127.   
    128. void print_MACRO_T(const MACRO_T* pArray, int cnt)  
    129. {  
    130.     int i;  
    131.     for( i = 0; i < cnt; ++i )  
    132.     {  
    133.         printf( "%s %s ", pArray[i].name, pArray[i].value );  
    134.     }  
    135.     printf( " " );  
    136. }  
    137.   
    138.   
    139. int main(int argc, char* argv[])  
    140. {  
    141.   
    142.     printf("testzintrin v1.00 (%dbit) ", GetProgramBits());  
    143.     print_MACRO_T(g_intrins, sizeof(g_intrins)/sizeof(g_intrins[0]));  
    144.   
    145.     // _mm_malloc  
    146. #ifdef INTRIN_SSE  
    147.     if(1)  
    148.     {  
    149.         void* p;  
    150.         p = _mm_malloc(0x10, 0x10);  
    151.         printf("_mm_malloc: %ph ", p);  
    152.         _mm_free(p);  
    153.     }  
    154. #endif  
    155.   
    156.     // mmx  
    157. #ifdef INTRIN_MMX  
    158.     _mm_empty();  
    159. #endif  
    160.   
    161.     // 3DNow!  
    162. #ifdef INTRIN_3dNOW  
    163.     //_m_femms();   // AMD cpu only.  
    164. #endif  
    165.   
    166.     // sse  
    167. #ifdef INTRIN_SSE  
    168.     if(1)  
    169.     {  
    170.         __m128 xmm1;  
    171.         float f;  
    172.         printf("&xmm1: %ph ", &xmm1);  
    173.         xmm1 = _mm_setzero_ps();    // SSE instruction: xorps  
    174.         f = _mm_cvtss_f32(xmm1);  
    175.         printf("_mm_cvtss_f32: %f ", f);  
    176.     }  
    177. #endif  
    178.   
    179.     // popcnt  
    180. #ifdef INTRIN_POPCNT  
    181.     printf("popcnt(0xffffffffu): %u ", _mm_popcnt_u32(0xffffffffu));  
    182. #endif  
    183.   
    184.     return 0;  
    185. }  


    3.3 makefile

      全部代码——

    [plain] view plaincopy
     
    1. # flags  
    2. CC = gcc  
    3. CFS = -Wall  
    4. LFS =   
    5.   
    6. # args  
    7. RELEASE =0  
    8. BITS =  
    9. CFLAGS = -msse  
    10.   
    11. # [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.  
    12. ifeq ($(RELEASE),0)  
    13.     # debug  
    14.     CFS += -g  
    15. else  
    16.     # release  
    17.     CFS += -static -O3 -DNDEBUG  
    18.     LFS += -static  
    19. endif  
    20.   
    21. # [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32.  
    22. ifeq ($(BITS),32)  
    23.     CFS += -m32  
    24.     LFS += -m32  
    25. else  
    26.     ifeq ($(BITS),64)  
    27.         CFS += -m64  
    28.         LFS += -m64  
    29.     else  
    30.     endif  
    31. endif  
    32.   
    33. # [args] 使用 CFLAGS 添加新的参数. make CFLAGS="-mpopcnt -msse4a".  
    34. CFS += $(CFLAGS)  
    35.   
    36.   
    37. .PHONY : all clean  
    38.   
    39. # files  
    40. TARGETS = testzintrin  
    41. OBJS = testzintrin.o  
    42.   
    43. all : $(TARGETS)  
    44.   
    45. testzintrin : $(OBJS)  
    46.     $(CC) $(LFS) -o $@ $^  
    47.   
    48.   
    49. testzintrin.o : testzintrin.c zintrin.h  
    50.     $(CC) $(CFS) -c $<  
    51.   
    52.   
    53. clean :  
    54.     rm -f $(OBJS) $(TARGETS) $(addsuffix .exe,$(TARGETS))  


    四、测试

      在以下编译器中成功编译——
    VC6:x86版。
    VC2003:x86版。
    VC2005:x86版、x64版。
    VC2010:x86版、x64版。
    GCC 4.7.0(Fedora 17 x64):x86版、x64版。
    GCC 4.6.2(MinGW(20120426)):x86版。
    GCC 4.6.1(TDM-GCC(MinGW-w64)):x86版、x64版。
    llvm-gcc-4.2(Mac OS X Lion 10.7.4, Xcode 4.4.1):x86版、x64版。



    参考文献——
    《Predefined Macros》. http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.110).aspx
    《Intrinsics头文件与SIMD指令集、Visual Studio版本对应表》. http://www.cnblogs.com/zyl910/archive/2012/02/28/vs_intrin_table.html
    《GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表》. http://www.cnblogs.com/zyl910/archive/2012/08/27/intrin_table_gcc.html

    源码下载—— 
    http://files.cnblogs.com/zyl910/zintrin.rar

  • 相关阅读:
    【转】CUDA5/CentOS6.4
    【转】centos 6.4 samba 安装配置
    【转】Install MATLAB 2013a on CentOS 6.4 x64 with mode silent
    【转】Getting xrdp to work on CentOS 6.4
    【VLFeat】使用matlab版本计算HOG
    Unofficial Windows Binaries for Python Extension Packages
    March 06th, 2018 Week 10th Tuesday
    March 05th, 2018 Week 10th Monday
    March 04th, 2018 Week 10th Sunday
    March 03rd, 2018 Week 9th Saturday
  • 原文地址:https://www.cnblogs.com/tibetanmastiff/p/4694367.html
Copyright © 2011-2022 走看看