[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

原文地址:https://www.cnblogs.com/tibetanmastiff/p/4694367.html