博客来源: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的“\lib\gcc\mingw32\4.6.2\include”子目录中。
  详见——
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

  全部代码——

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

3.2 testzintrin.c

  全部代码——

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

3.3 makefile

  全部代码——

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

[C] zintrin.h : 智能引入intrinsic函数。支持VC、GCC,兼容Windows、Linux、Mac OS X的更多相关文章

  1. Sublime Text3注册及汉化(支持Windows、MAC OS)

    苹果mac 版本下载 点击下载https://download.sublimetext.com/Sublime%20Text%20Build%203114.dmg PART_A 注册 v3103及以上 ...

  2. pycharm2020.2破解版教程激活码支持Windows Linux Mac系统-中关村老大爷

    听说很多朋友想要PyCharm专业版2020.2的破解教程.现在来了,亲测破解成功.支持mac linux windows系统.本教程提供官方安装包.激活码和注册补丁. 本教程仅供学习和讨论,禁止商业 ...

  3. 解决Mac OS X 系统在home文件夹下面操作不支持的方法

    解决Mac OS X 系统在home文件夹下面操作不支持的方法   最近需要使用Mac OS X 系统尝试安装使用appium程序,安装过程中发现,Mac OS X 系统在home文件夹下面操作不支持 ...

  4. SQL汉字转拼音函数-支持首字母、全拼

    SQL汉字转拼音函数-支持首字母.全拼 FROM :http://my.oschina.net/ind/blog/191659 作者不详 --方法一sqlserver汉字转拼音首字母 --调用方法 s ...

  5. 彻底弄清c标准库中string.h里的常用函数用法

    在我们平常写的c/c++程序,一些算法题中,我们常常会用到c标准库中string.h文件中的函数,这些函数主要用于处理内存,字符串相关操作,是很有用的工具函数.而且有些时候,在笔试或面试中也会出现让你 ...

  6. 让bind函数支持IE8浏览器的方法

    bind函数在IE8下是不支持的,只需要在你的js文件中加入如下代码就可以支持IE8 //让bind函数支持IE8 if (!Function.prototype.bind) { Function.p ...

  7. 重构改善既有代码设计--重构手法16:Introduce Foreign Method (引入外加函数)&& 重构手法17:Introduce Local Extension (引入本地扩展)

    重构手法16:Introduce Foreign Method (引入外加函数)你需要为提供服务的类增加一个函数,但你无法修改这个类.在客户类中建立一个函数,并以第一参数形式传入一个服务类实例. 动机 ...

  8. Python 3.x 引入了函数注释

    Python 3.x 引入了函数注释,以增强函数的注释功能,下面是一个普通的自定义函数:   def dog(name, age, species):   return (name, age, spe ...

  9. PCL智能指针疑云 <三> 智能指针作为函数的传值参数和传引用参数

    一 函数的参数传递可以简单分类为“传值”和“传引用”. 声明函数时,形参带引用“&”,则函数调用时,是把实参所在的内存直接传给函数所开辟的栈内存.在函数内对形参的修改相当于对实参也进行修改. ...

随机推荐

  1. stm32 dac库函数解读

    1.简述: 12位数字输入,电压输出,DAC可以配置为8位或12位模式.有2个输出通道.在双DAC模式下,两个通道可以独立地工作. 特殊功能: 噪声波形生成,三角波形生成,外部触发转换,双DAC同时或 ...

  2. Http Framework

    http request clientVolley https://android.googlesource.com/platform/frameworks/volley聚划算用的litehttp h ...

  3. Java三大主流开源工作流引擎技术分析

    首先,这个评论是我从网上,书中,搜索和整理出来的,也许有技术点上的错误点,也许理解没那么深入.但是我是秉着学习的态度加以评论,学习,希望对大家有用,进入正题! 三大主流工作流引擎:Shark,oswo ...

  4. HDU 圆桌会议 - 数学题

    圆桌   题意就是每分钟可以将相邻的两个人的位置互换一下 , 问你 ,几分钟可以将所有人的位置互换成    原先的  B 在A的右边 C在A的左边 , 换成现在的 C 在A 的右边 , B 在 A 的 ...

  5. magento在产品详细页面添加分享链接的方法

    1,在产品详细页面的对用位置加入一下代码 <div class="sharethis_box">          <?php echo $this->ge ...

  6. 收藏:关于UseSubmitBehavior和OnClientClick同时使用,导致无法触发后台事件的问题

    经常会有正样的需求,在用户做一个不易恢复并且容易误操作的动作时需要给用户以提示,用户确认后继续执行动作. 简单的解决方案是:前台用OnClientClick事件中使用Confirm给用弹出确认提示框, ...

  7. Java 反射 ParameterizedType 参数化类型

    /***************************************************************************************** * Java 反射 ...

  8. "Your local changes to the following files would be overwritten by merge" on git

    运行: git merge --ff origin/master 得到错误信息: error: Your local changes to the following files would be o ...

  9. 常用的Hash算法

    1.RSHash unsigned int RSHash(const std::string& str) {    unsigned int b    = 378551;    unsigne ...

  10. The Implementation of Lua 5.0 阅读笔记(一)

    没想到Lua的作者理论水平这么高,这篇文章读的我顿生高屋建瓴之感.云风分享了一篇中译:http://www.codingnow.com/2000/download/The%20Implementati ...