• [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

  • 相关阅读:
    Ueeidor 使用
    springMvc 拦截器
    redis 设置密码
    freemarker 定义公共header
    freemarker macro 使用
    freemarker ! 用法
    Android 远程连接数据库。。。。。
    Android Studio 配置
    Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
    表单,table的css
  • 原文地址:https://www.cnblogs.com/tibetanmastiff/p/4694367.html
Copyright © 2020-2023  润新知