新版本——
http://www.cnblogs.com/zyl910/archive/2012/10/01/zintrin_v101.html
作者:zyl910。
现在很多编译器支持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 --target-help 下列选项与特定目标机相关: -m128bit-long-double sizeof(long double) 等于 16 -m32 生成 32 位 i386 代码 -m3dnow 支持 3DNow! 内建函数 -m64 生成 64 位 x86-64 代码 -m80387 使用硬件浮点单元 -m8bit-idiv 将 32/64 位整数除法扩展为带运行时检查的 8 位无符号整数除法 -m96bit-long-double sizeof(long double) 等于 12 -mabi= 生成遵循给定 ABI 的代码 -mabm 支持生成高级位操作(ABM)指令。 -maccumulate-outgoing-args 在函数序言中为输出参数预留空间 -maes 支持 AES 内建函数及代码生成 -malign-double 一些双精度浮点数在双字边界上对齐 -malign-functions= 函数入口对齐在 2 的此次方上 -malign-jumps= 跳转目标对齐在 2 的此次方上 -malign-loops= 循环代码对齐到 2 的此次方上 -malign-stringops 对齐字符串操作的目标 -mandroid 为 Android 操作系统生成代码。 -march= 为给定的 CPU 生成代码 -masm= 使用给定的汇编风格 -mavx 支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2 和 AVX 内建函数及代码生成 -mavx2 Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX and AVX2 built-in functions and code generation -mavx256-split-unaligned-load Split 32-byte AVX unaligned load -mavx256-split-unaligned-store Split 32-byte AVX unaligned store -mbionic 使用 Bionic C 标准库 -mbmi 支持 BMI 内建函数及代码生成 -mbmi2 Support BMI2 built-in functions and code generation -mbranch-cost= 指定分支的代价(1-5,任意单位) -mcld 在函数序言中生成 cld 指令 -mcmodel= 使用给定的 x86-64 代码模式 -mcrc32 支持生成 crc32 指令。 -mcx16 支持生成 cmpxchg16b 指令。 -mdispatch-scheduler Do dispatch scheduling if processor is bdver1 or bdver2 and Haifa scheduling is selected. -mf16c 支持 F16C 内建函数及代码生成 -mfancy-math-387 为 FPU 生成 sin、cos 和 sqrt 指令 -mfentry Emit profiling counter call at function entry before prologue. -mfma 支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1、SSE4.2、AVX 和 FMA 内建函数及代码生成 -mfma4 支持 FMA4 内建函数及代码生成 -mforce-drap 总是使用动态对齐参数指针(DRAP)来重新对齐堆栈 -mfp-ret-in-387 在 FPU 寄存器中存放函数返回值 -mfpmath= 为指定的指令集生成浮点数学代码 -mfsgsbase 支持 FSGSBASE 内建函数及代码生成 -mglibc 使用 GNU C 标准库 -mhard-float 使用硬件浮点单元 -mieee-fp 浮点数间的比较严格遵循 IEEE 标准 -mincoming-stack-boundary= 假定栈对齐到 2 的此次方上 -minline-all-stringops 内联所有已知的字符串操作 -minline-stringops-dynamically 内联 memset/memcpy 字符串操作,但仅为较小的块使用内联版本 -mlarge-data-threshold= 在 x86-64 中等模式下大于指定阈值的数据将被存放在 .ldata 节中 -mlwp 支持 LWP 内建函数及代码生成 -mlzcnt Support LZCNT built-in function and code generation -mmmx 支持 MMX 内建函数 -mmovbe 支持生成 movbe 指令。 -mms-bitfields 使用本地 (MS) 位段存储方式 -mno-sse4 不支持 SSE4.1 和 SSE4.2 内建函数及代码生成 -momit-leaf-frame-pointer 为叶函数(不调用其他函数的函数)忽略框架指针 -mpc32 Set 80387 floating-point precision to 32-bit -mpc64 Set 80387 floating-point precision to 64-bit -mpc80 Set 80387 floating-point precision to 80-bit -mpclmul 支持 PCLMUL 内建函数及代码生成 -mpopcnt 支持生成 popcnt 指令。 -mprefer-avx128 自动向量化时使用 128 位 AVX 指令而不是 256 位 AVX 指令 -mpreferred-stack-boundary= 试图让栈保持对齐到 2 的此次方上 -mpush-args 使用 push 指令保存输出参数 -mrdrnd 支持 RDRND 内建函数及代码生成 -mrecip 生成倒数指令而不是 divss 和 sqrtss。 -mrecip= Control generation of reciprocal estimates. -mred-zone 在 x86-64 代码中使用红区 -mregparm= 用以传递整数参数的寄存器个数 -mrtd 更改调用约定 -msahf 支持在 x86-64 模式下生成 sahf 指令。 -msoft-float 不使用硬件浮点单元 -msse 支持 MMX 和 SSE 内建函数及代码生成 -msse2 支持 MMX、SSE 和 SSE2 内建函数及代码生成 -msse2avx 支持带 VEX 前缀的 SSE 指令 -msse3 支持 MMX、SSE、SSE2 和 SSE3 内建函数及代码生成 -msse4 支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1 和 SSE4.2 内建函数及代码生成 -msse4.1 支持 MMX、SSE、SSE2、SSE3、SSSE3 和 SSE4.1 内建函数及代码生成 -msse4.2 支持 MMX、SSE、SSE2、SSE3、SSSE3、SSE4.1 和 SSE4.2 内建函数及代码生成 -msse4a 支持 MMX、SSE、SSE2、SSE3 和 SSE4A 内建函数及代码生成 -msseregparm 在 SF 和 DF 模式下使用 SSE 寄存器调用约定 -mssse3 支持 MMX、SSE、SSE2、SSE3 和 SSSE3 内建函数及代码生成 -mstack-arg-probe 启用堆栈探测 -mstackrealign 在前言中重新对齐堆栈 -mstringop-strategy= 选择生成字符串操作的策略 -mtbm 支持 TBM 内建函数及代码生成 -mtls-dialect= 使用给定的线程局部存储模式 -mtls-direct-seg-refs 当访问线程局部数据时直接引用 %gs -mtune= 为指定的 CPU 优化代码 -muclibc 使用 uClibc C 标准库 -mveclibabi= 指定要使用的向量库 ABI -mvect8-ret-in-mem 将 8 字节向量在内存中返回 -mvzeroupper Generate vzeroupper instruction before a transfer of control flow out of the function. -mx32 Generate 32bit x86-64 code -mxop 支持 XOP 内建函数及代码生成 Known assembler dialects (for use with the -masm-dialect= option): att intel Known ABIs (for use with the -mabi= option): ms sysv Known code models (for use with the -mcmodel= option): 32 kernel large medium small Valid arguments to -mfpmath=: 387 387+sse 387,sse both sse sse+387 sse,387 Known vectorization library ABIs (for use with the -mveclibabi= option): acml svml Valid arguments to -mstringop-strategy=: byte_loop libcall loop rep_4byte rep_8byte rep_byte unrolled_loop Known TLS dialects (for use with the -mtls-dialect= option): gnu gnu2 汇编器选项 ================= 使用“-Wa,选项”将“选项”传递给汇编器。 -Q ignored -V print assembler version number -k ignored -n Do not optimize code alignment -q quieten some warnings -s ignored --32/--64/--x32 generate 32bit/64bit/x32 code --divide ignored -march=CPU[,+EXTENSION...] generate code for CPU and EXTENSION, CPU is one of: generic32, generic64, i386, i486, i586, i686, pentium, pentiumpro, pentiumii, pentiumiii, pentium4, prescott, nocona, core, core2, corei7, l1om, k1om, k6, k6_2, athlon, opteron, k8, amdfam10, bdver1, bdver2 EXTENSION is combination of: 8087, 287, 387, no87, mmx, nommx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, sse4, nosse, avx, avx2, noavx, vmx, vmfunc, smx, xsave, xsaveopt, aes, pclmul, fsgsbase, rdrnd, f16c, bmi2, fma, fma4, xop, lwp, movbe, ept, lzcnt, hle, rtm, invpcid, clflush, nop, syscall, rdtscp, 3dnow, 3dnowa, padlock, svme, sse4a, abm, bmi, tbm -mtune=CPU optimize for CPU, CPU is one of: generic32, generic64, i8086, i186, i286, i386, i486, i586, i686, pentium, pentiumpro, pentiumii, pentiumiii, pentium4, prescott, nocona, core, core2, corei7, l1om, k1om, k6, k6_2, athlon, opteron, k8, amdfam10, bdver1, bdver2 -msse2avx encode SSE instructions with VEX prefix -msse-check=[none|error|warning] check SSE instructions -mavxscalar=[128|256] encode scalar AVX instructions with specific vector length -mmnemonic=[att|intel] use AT&T/Intel mnemonic -msyntax=[att|intel] use AT&T/Intel syntax -mindex-reg support pseudo index registers -mnaked-reg don't require `%' prefix for registers -mold-gcc support old (<= 2.8.1) versions of gcc 链接器选项 ============== 使用“-Wl,选项”将“选项”传递给链接器。 elf_x86_64: --audit=AUDITLIB Specify a library to use for auditing -Bgroup Selects group name lookup rules for DSO --build-id[=STYLE] Generate build ID note -P AUDITLIB, --depaudit=AUDITLIB Specify a library to use for auditing dependencies --disable-new-dtags Disable new dynamic tags --enable-new-dtags Enable new dynamic tags --eh-frame-hdr Create .eh_frame_hdr section --exclude-libs=LIBS Make all symbols in LIBS hidden --hash-style=STYLE Set hash style to sysv, gnu or both -z combreloc Merge dynamic relocs into one section and sort -z common-page-size=SIZE Set common page size to SIZE -z defs Report unresolved symbols in object files. -z execstack Mark executable as requiring executable stack -z initfirst Mark DSO to be initialized first at runtime -z interpose Mark object to interpose all DSOs but executable -z lazy Mark object lazy runtime binding (default) -z loadfltr Mark object requiring immediate process -z max-page-size=SIZE Set maximum page size to SIZE -z muldefs Allow multiple definitions -z nocombreloc Don't merge dynamic relocs into one section -z nocopyreloc Don't create copy relocs -z nodefaultlib Mark object not to use default search paths -z nodelete Mark DSO non-deletable at runtime -z nodlopen Mark DSO not available to dlopen -z nodump Mark DSO not available to dldump -z noexecstack Mark executable as not requiring executable stack -z norelro Don't create RELRO program header -z now Mark object non-lazy runtime binding -z origin Mark object requiring immediate $ORIGIN processing at runtime -z relro Create RELRO program header --ld-generated-unwind-info Generate exception handling info for PLT. --no-ld-generated-unwind-info Don't do so. elf32_x86_64: --audit=AUDITLIB Specify a library to use for auditing -Bgroup Selects group name lookup rules for DSO --build-id[=STYLE] Generate build ID note -P AUDITLIB, --depaudit=AUDITLIB Specify a library to use for auditing dependencies --disable-new-dtags Disable new dynamic tags --enable-new-dtags Enable new dynamic tags --eh-frame-hdr Create .eh_frame_hdr section --exclude-libs=LIBS Make all symbols in LIBS hidden --hash-style=STYLE Set hash style to sysv, gnu or both -z combreloc Merge dynamic relocs into one section and sort -z common-page-size=SIZE Set common page size to SIZE -z defs Report unresolved symbols in object files. -z execstack Mark executable as requiring executable stack -z initfirst Mark DSO to be initialized first at runtime -z interpose Mark object to interpose all DSOs but executable -z lazy Mark object lazy runtime binding (default) -z loadfltr Mark object requiring immediate process -z max-page-size=SIZE Set maximum page size to SIZE -z muldefs Allow multiple definitions -z nocombreloc Don't merge dynamic relocs into one section -z nocopyreloc Don't create copy relocs -z nodefaultlib Mark object not to use default search paths -z nodelete Mark DSO non-deletable at runtime -z nodlopen Mark DSO not available to dlopen -z nodump Mark DSO not available to dldump -z noexecstack Mark executable as not requiring executable stack -z norelro Don't create RELRO program header -z now Mark object non-lazy runtime binding -z origin Mark object requiring immediate $ORIGIN processing at runtime -z relro Create RELRO program header --ld-generated-unwind-info Generate exception handling info for PLT. --no-ld-generated-unwind-info Don't do so. elf_i386: --audit=AUDITLIB Specify a library to use for auditing -Bgroup Selects group name lookup rules for DSO --build-id[=STYLE] Generate build ID note -P AUDITLIB, --depaudit=AUDITLIB Specify a library to use for auditing dependencies --disable-new-dtags Disable new dynamic tags --enable-new-dtags Enable new dynamic tags --eh-frame-hdr Create .eh_frame_hdr section --exclude-libs=LIBS Make all symbols in LIBS hidden --hash-style=STYLE Set hash style to sysv, gnu or both -z combreloc Merge dynamic relocs into one section and sort -z common-page-size=SIZE Set common page size to SIZE -z defs Report unresolved symbols in object files. -z execstack Mark executable as requiring executable stack -z initfirst Mark DSO to be initialized first at runtime -z interpose Mark object to interpose all DSOs but executable -z lazy Mark object lazy runtime binding (default) -z loadfltr Mark object requiring immediate process -z max-page-size=SIZE Set maximum page size to SIZE -z muldefs Allow multiple definitions -z nocombreloc Don't merge dynamic relocs into one section -z nocopyreloc Don't create copy relocs -z nodefaultlib Mark object not to use default search paths -z nodelete Mark DSO non-deletable at runtime -z nodlopen Mark DSO not available to dlopen -z nodump Mark DSO not available to dldump -z noexecstack Mark executable as not requiring executable stack -z norelro Don't create RELRO program header -z now Mark object non-lazy runtime binding -z origin Mark object requiring immediate $ORIGIN processing at runtime -z relro Create RELRO program header --ld-generated-unwind-info Generate exception handling info for PLT. --no-ld-generated-unwind-info Don't do so. elf_l1om: --audit=AUDITLIB Specify a library to use for auditing -Bgroup Selects group name lookup rules for DSO --build-id[=STYLE] Generate build ID note -P AUDITLIB, --depaudit=AUDITLIB Specify a library to use for auditing dependencies --disable-new-dtags Disable new dynamic tags --enable-new-dtags Enable new dynamic tags --eh-frame-hdr Create .eh_frame_hdr section --exclude-libs=LIBS Make all symbols in LIBS hidden --hash-style=STYLE Set hash style to sysv, gnu or both -z combreloc Merge dynamic relocs into one section and sort -z common-page-size=SIZE Set common page size to SIZE -z defs Report unresolved symbols in object files. -z execstack Mark executable as requiring executable stack -z initfirst Mark DSO to be initialized first at runtime -z interpose Mark object to interpose all DSOs but executable -z lazy Mark object lazy runtime binding (default) -z loadfltr Mark object requiring immediate process -z max-page-size=SIZE Set maximum page size to SIZE -z muldefs Allow multiple definitions -z nocombreloc Don't merge dynamic relocs into one section -z nocopyreloc Don't create copy relocs -z nodefaultlib Mark object not to use default search paths -z nodelete Mark DSO non-deletable at runtime -z nodlopen Mark DSO not available to dlopen -z nodump Mark DSO not available to dldump -z noexecstack Mark executable as not requiring executable stack -z norelro Don't create RELRO program header -z now Mark object non-lazy runtime binding -z origin Mark object requiring immediate $ORIGIN processing at runtime -z relro Create RELRO program header --ld-generated-unwind-info Generate exception handling info for PLT. --no-ld-generated-unwind-info Don't do so. elf_k1om: --audit=AUDITLIB Specify a library to use for auditing -Bgroup Selects group name lookup rules for DSO --build-id[=STYLE] Generate build ID note -P AUDITLIB, --depaudit=AUDITLIB Specify a library to use for auditing dependencies --disable-new-dtags Disable new dynamic tags --enable-new-dtags Enable new dynamic tags --eh-frame-hdr Create .eh_frame_hdr section --exclude-libs=LIBS Make all symbols in LIBS hidden --hash-style=STYLE Set hash style to sysv, gnu or both -z combreloc Merge dynamic relocs into one section and sort -z common-page-size=SIZE Set common page size to SIZE -z defs Report unresolved symbols in object files. -z execstack Mark executable as requiring executable stack -z initfirst Mark DSO to be initialized first at runtime -z interpose Mark object to interpose all DSOs but executable -z lazy Mark object lazy runtime binding (default) -z loadfltr Mark object requiring immediate process -z max-page-size=SIZE Set maximum page size to SIZE -z muldefs Allow multiple definitions -z nocombreloc Don't merge dynamic relocs into one section -z nocopyreloc Don't create copy relocs -z nodefaultlib Mark object not to use default search paths -z nodelete Mark DSO non-deletable at runtime -z nodlopen Mark DSO not available to dlopen -z nodump Mark DSO not available to dldump -z noexecstack Mark executable as not requiring executable stack -z norelro Don't create RELRO program header -z now Mark object non-lazy runtime binding -z origin Mark object requiring immediate $ORIGIN processing at runtime -z relro Create RELRO program header --ld-generated-unwind-info Generate exception handling info for PLT. --no-ld-generated-unwind-info Don't do so.
当通过命令行参数打开指令集支持后,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
全部代码——
#ifndef __ZINTRIN_H_INCLUDED #define __ZINTRIN_H_INCLUDED // 根据不同的编译器做不同的处理. #if defined(__GNUC__) // GCC #if (defined(__i386__) || defined(__x86_64__) ) && !defined(__llvm__) // Mac OS X 的 llvm 不支持 intrin 函数. // header files #include <x86intrin.h> #include <cpuid.h> // macros #ifdef __MMX__ #define INTRIN_MMX 1 #endif #ifdef __3dNOW__ #define INTRIN_3dNOW 1 #endif #ifdef __SSE__ #define INTRIN_SSE 1 #endif #ifdef __SSE2__ #define INTRIN_SSE2 1 #endif #ifdef __SSE3__ #define INTRIN_SSE3 1 #endif #ifdef __SSSE3__ #define INTRIN_SSSE3 1 #endif #ifdef __SSE4_1__ #define INTRIN_SSE4_1 1 #endif #ifdef __SSE4_2__ #define INTRIN_SSE4_2 1 #endif #ifdef __SSE4A__ #define INTRIN_SSE4A 1 #endif #ifdef __AES__ #define INTRIN_AES 1 #endif #ifdef __PCLMUL__ #define INTRIN_PCLMUL 1 #endif #ifdef __AVX__ #define INTRIN_AVX 1 #endif #ifdef __AVX2__ #define INTRIN_AVX2 1 #endif #ifdef __F16C__ #define INTRIN_F16C 1 #endif #ifdef __FMA__ #define INTRIN_FMA 1 #endif #ifdef __FMA4__ #define INTRIN_FMA4 1 #endif #ifdef __XOP__ #define INTRIN_XOP 1 #endif #ifdef __LWP__ #define INTRIN_LWP 1 #endif #ifdef __RDRND__ #define INTRIN_RDRND 1 #endif #ifdef __FSGSBASE__ #define INTRIN_FSGSBASE 1 #endif #ifdef __LZCNT__ #define INTRIN_LZCNT 1 #endif #ifdef __POPCNT__ #define INTRIN_POPCNT 1 #endif #ifdef __BMI__ #define INTRIN_BMI 1 #endif #ifdef __BMI2__ #define INTRIN_BMI2 1 #endif #ifdef __TBM__ #define INTRIN_TBM 1 #endif #endif // #if !defined(__llvm__) #elif defined(_MSC_VER) // MSVC // header files #if _MSC_VER >=1400 // VC2005 #include <intrin.h> #elif _MSC_VER >=1200 // VC6 #if (defined(_M_IX86) || defined(_M_X64)) #include <emmintrin.h> // MMX, SSE, SSE2 #include <mm3dnow.h> // 3DNow! #endif #endif // #if _MSC_VER >=1400 #include <malloc.h> // _mm_malloc, _mm_free. // macros #if (defined(_M_IX86) || defined(_M_X64)) #if _MSC_VER >=1200 // VC6 #if defined(_M_X64) && !defined(__INTEL_COMPILER) // VC编译器不支持64位下的MMX. #else #define INTRIN_MMX 1 // mmintrin.h #define INTRIN_3dNOW 1 // mm3dnow.h #endif #define INTRIN_SSE 1 // xmmintrin.h #define INTRIN_SSE2 1 // emmintrin.h #endif #if _MSC_VER >=1300 // VC2003 #endif #if _MSC_VER >=1400 // VC2005 #endif #if _MSC_VER >=1500 // VC2008 #define INTRIN_SSE3 1 // pmmintrin.h #define INTRIN_SSSE3 1 // tmmintrin.h #define INTRIN_SSE4_1 1 // smmintrin.h #define INTRIN_SSE4_2 1 // nmmintrin.h #define INTRIN_SSE4A 1 // intrin.h #define INTRIN_LZCNT 1 // intrin.h #define INTRIN_POPCNT 1 // nmmintrin.h #endif #if _MSC_VER >=1600 // VC2010 #define INTRIN_AES 1 // wmmintrin.h #define INTRIN_PCLMUL 1 // wmmintrin.h #define INTRIN_AVX 1 // immintrin.h #define INTRIN_FMA4 1 // ammintrin.h #define INTRIN_XOP 1 // ammintrin.h #define INTRIN_LWP 1 // ammintrin.h #endif #if _MSC_VER >=1700 // VC2012 #define INTRIN_AVX2 0 //TODO:待查证. 先设为0. #define INTRIN_FMA 0 #define INTRIN_F16C 0 #define INTRIN_RDRND 0 #define INTRIN_FSGSBASE 0 #define INTRIN_BMI 0 #define INTRIN_BMI2 0 #define INTRIN_TBM 0 #endif #endif //TODO:待查证 VS配合intel C编译器时intrin函数的支持性. // VC2008之前没有_mm_cvtss_f32 #if _MSC_VER <1500 // VC2008 // float _mm_cvtss_f32(__m128 _A); #ifndef _mm_cvtss_f32 #define _mm_cvtss_f32(__m128_A) ( *(float*)(void*)&(__m128_A) ) #endif #endif #else //#error Only supports GCC or MSVC. #endif // #if defined(__GNUC__) #endif // #ifndef __ZINTRIN_H_INCLUDED
3.2 testzintrin.c
全部代码——
#include <stdio.h> #include "zintrin.h" #define PT_MAKE_STR(x) { #x, PT_MAKE_STR_ESC(x) } #define PT_MAKE_STR_ESC(x) #x typedef struct tagMACRO_T { const char *name; const char *value; } MACRO_T; /* Intrinsics */ const MACRO_T g_intrins[] = { {"[Intrinsics]", ""}, #ifdef INTRIN_MMX PT_MAKE_STR(INTRIN_MMX), #endif #ifdef INTRIN_3dNOW PT_MAKE_STR(INTRIN_3dNOW), #endif #ifdef INTRIN_SSE PT_MAKE_STR(INTRIN_SSE), #endif #ifdef INTRIN_SSE2 PT_MAKE_STR(INTRIN_SSE2), #endif #ifdef INTRIN_SSE3 PT_MAKE_STR(INTRIN_SSE3), #endif #ifdef INTRIN_SSSE3 PT_MAKE_STR(INTRIN_SSSE3), #endif #ifdef INTRIN_SSE4_1 PT_MAKE_STR(INTRIN_SSE4_1), #endif #ifdef INTRIN_SSE4_2 PT_MAKE_STR(INTRIN_SSE4_2), #endif #ifdef INTRIN_SSE4A PT_MAKE_STR(INTRIN_SSE4A), #endif #ifdef INTRIN_AES PT_MAKE_STR(INTRIN_AES), #endif #ifdef INTRIN_PCLMUL PT_MAKE_STR(INTRIN_PCLMUL), #endif #ifdef INTRIN_AVX PT_MAKE_STR(INTRIN_AVX), #endif #ifdef INTRIN_AVX2 PT_MAKE_STR(INTRIN_AVX2), #endif #ifdef INTRIN_F16C PT_MAKE_STR(INTRIN_F16C), #endif #ifdef INTRIN_FMA PT_MAKE_STR(INTRIN_FMA), #endif #ifdef INTRIN_FMA4 PT_MAKE_STR(INTRIN_FMA4), #endif #ifdef INTRIN_XOP PT_MAKE_STR(INTRIN_XOP), #endif #ifdef INTRIN_LWP PT_MAKE_STR(INTRIN_LWP), #endif #ifdef INTRIN_RDRND PT_MAKE_STR(INTRIN_RDRND), #endif #ifdef INTRIN_FSGSBASE PT_MAKE_STR(INTRIN_FSGSBASE), #endif #ifdef INTRIN_LZCNT PT_MAKE_STR(INTRIN_LZCNT), #endif #ifdef INTRIN_POPCNT PT_MAKE_STR(INTRIN_POPCNT), #endif #ifdef INTRIN_BMI PT_MAKE_STR(INTRIN_BMI), #endif #ifdef INTRIN_BMI2 PT_MAKE_STR(INTRIN_BMI2), #endif #ifdef INTRIN_TBM PT_MAKE_STR(INTRIN_TBM), #endif }; // 获取程序位数(被编译为多少位的代码) int GetProgramBits(void) { return sizeof(int*) * 8; } void print_MACRO_T(const MACRO_T* pArray, int cnt) { int i; for( i = 0; i < cnt; ++i ) { printf( "%s\t%s\n", pArray[i].name, pArray[i].value ); } printf( "\n" ); } int main(int argc, char* argv[]) { printf("testzintrin v1.00 (%dbit)\n\n", GetProgramBits()); print_MACRO_T(g_intrins, sizeof(g_intrins)/sizeof(g_intrins[0])); // _mm_malloc #ifdef INTRIN_SSE if(1) { void* p; p = _mm_malloc(0x10, 0x10); printf("_mm_malloc:\t%ph\n", p); _mm_free(p); } #endif // mmx #ifdef INTRIN_MMX _mm_empty(); #endif // 3DNow! #ifdef INTRIN_3dNOW //_m_femms(); // AMD cpu only. #endif // sse #ifdef INTRIN_SSE if(1) { __m128 xmm1; float f; printf("&xmm1:\t%ph\n", &xmm1); xmm1 = _mm_setzero_ps(); // SSE instruction: xorps f = _mm_cvtss_f32(xmm1); printf("_mm_cvtss_f32:\t%f\n", f); } #endif // popcnt #ifdef INTRIN_POPCNT printf("popcnt(0xffffffffu):\t%u\n", _mm_popcnt_u32(0xffffffffu)); #endif return 0; }
3.3 makefile
全部代码——
# flags CC = gcc CFS = -Wall LFS = # args RELEASE =0 BITS = CFLAGS = -msse # [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1. ifeq ($(RELEASE),0) # debug CFS += -g else # release CFS += -static -O3 -DNDEBUG LFS += -static endif # [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32. ifeq ($(BITS),32) CFS += -m32 LFS += -m32 else ifeq ($(BITS),64) CFS += -m64 LFS += -m64 else endif endif # [args] 使用 CFLAGS 添加新的参数. make CFLAGS="-mpopcnt -msse4a". CFS += $(CFLAGS) .PHONY : all clean # files TARGETS = testzintrin OBJS = testzintrin.o all : $(TARGETS) testzintrin : $(OBJS) $(CC) $(LFS) -o $@ $^ testzintrin.o : testzintrin.c zintrin.h $(CC) $(CFS) -c $< clean : 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