新版本——
http://www.cnblogs.com/zyl910/archive/2012/11/07/zintrin_v102.html
[C] zintrin.h: 智能引入intrinsic函數 V1.02版。支持VC2012,增加INTRIN_ALIGN、INTRIN_COMPILER_NAME宏
作者:zyl910。
之前的zintrin V1.00版對Mac OS X平台支持性不佳。現在的V1.01版改進對Mac OS X的支持,還做了這些改進——增加INTRIN_WORDSIZE宏 等。
一、更新說明
1.1 改進對Mac OS X的支持
前幾天在使用Xcode時,發現mac下也有intrinsic頭文件,詳見http://www.cnblogs.com/zyl910/archive/2012/09/27/intrin_mac.html。
但是在終端中調用gcc編譯時,總是報告找不到cpuid.h、x86intrin.h等文件。
后來才發現,“使用Xcode” 與 “終端中調用gcc”這兩種情況下,編譯器的include目錄是不同的——
使用Xcode:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/4.0/include
終端中調用gcc:/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/include
“/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/include”文件夾下的intrinsic頭文件版本較老,沒有提供cpuid.h、x86intrin.h等新版本的文件。
該怎么辦呢?
方案一:用宏判斷gcc的版本,然后引入最高支持的intrinsic頭文件。
方案二:強制要求用戶配置好include目錄。
方案三:根據__MMX__等宏判斷當前編譯器是否啟用該指令集,然后引入相應文件。
方案一不行。因為現在gcc的版本一樣,只是include目錄不同。
方案二也不好。因為這樣給使用帶來了麻煩。
方案三不錯。按需引入對應的文件。當發現啟用AVX等新指令集時,x86intrin.h應該是可用的。
1.2 增加INTRIN_WORDSIZE宏
某些intrinsic函數僅在64位下可用,例如_mm_popcnt_u64。所以需要一種能判斷目標機器字長的辦法。
一般情況下,指針變量的長度就是機器字長,但是sizeof運算符不能用於宏預處理。
這是可以利用C99的stdint.h,它提供了一系列宏用於判斷各種數據類型的范圍。其中有PTRDIFF_MAX,我們可以利用它來判斷機器字長——
// INTRIN_WORDSIZE: 目標機器的字長. #if PTRDIFF_MAX >= INT64_MAX #define INTRIN_WORDSIZE 64 #elif PTRDIFF_MAX >= INT32_MAX #define INTRIN_WORDSIZE 32 #else #define INTRIN_WORDSIZE 16 #endif
有一點需要注意,根據C99標准——
對於純C程序來說,PTRDIFF_MAX等宏默認是啟用的。
但對於C++程序,PTRDIFF_MAX等宏默認不啟用。只有在引入stdint.h之前定義了__STDC_LIMIT_MACROS,才會啟用這些宏。
當發現沒有PTRDIFF_MAX等宏時,可以這樣報告錯誤——
#include "stdint.h" #if !defined(PTRDIFF_MAX) || !defined(INT32_MAX) #error Need C99 marcos: __STDC_LIMIT_MACROS. #endif
1.3 其他改進
gcc不再引入cpuid.h。這是因為難以判斷cpuid.h是否存在。而使用ccpuid模塊能更方便的判斷cpu指令集。
不再假定未來VC版本支持AVX2等指令集。這是為了避免潛在的錯誤,寧缺毋濫。
調整了一下INTRIN_常數的順序。
二、全部代碼
2.1 zintrin.h
全部代碼——

#ifndef __ZINTRIN_H_INCLUDED #define __ZINTRIN_H_INCLUDED #include "stdint.h" #if !defined(PTRDIFF_MAX) || !defined(INT32_MAX) #error Need C99 marcos: __STDC_LIMIT_MACROS. #endif // INTRIN_WORDSIZE: 目標機器的字長. #if PTRDIFF_MAX >= INT64_MAX #define INTRIN_WORDSIZE 64 #elif PTRDIFF_MAX >= INT32_MAX #define INTRIN_WORDSIZE 32 #else #define INTRIN_WORDSIZE 16 #endif // 根據不同的編譯器做不同的處理. #if defined(__GNUC__) // GCC #if (defined(__i386__) || defined(__x86_64__) ) // header files //#include <cpuid.h> // mac下有時找不到. 於是放棄, 使用ccpuid模塊會更方便. //#include <x86intrin.h> // mac下有時找不到. 於是根據宏來加載頭文件. // macros #ifdef __MMX__ #define INTRIN_MMX 1 #include <mmintrin.h> #endif #ifdef __3dNOW__ #define INTRIN_3dNOW 1 #include <mm3dnow.h> #endif #ifdef __SSE__ #define INTRIN_SSE 1 #include <xmmintrin.h> #endif #ifdef __SSE2__ #define INTRIN_SSE2 1 #include <emmintrin.h> #endif #ifdef __SSE3__ #define INTRIN_SSE3 1 #include <pmmintrin.h> #endif #ifdef __SSSE3__ #define INTRIN_SSSE3 1 #include <tmmintrin.h> #endif #ifdef __SSE4_1__ #define INTRIN_SSE4_1 1 #include <smmintrin.h> #endif #ifdef __SSE4_2__ #define INTRIN_SSE4_2 1 #include <nmmintrin.h> #endif #ifdef __SSE4A__ #define INTRIN_SSE4A 1 #include <ammintrin.h> #endif #ifdef __AES__ #define INTRIN_AES 1 #include <x86intrin.h> #endif #ifdef __PCLMUL__ #define INTRIN_PCLMUL 1 #include <x86intrin.h> #endif #ifdef __AVX__ #define INTRIN_AVX 1 #include <x86intrin.h> #endif #ifdef __AVX2__ #define INTRIN_AVX2 1 #include <x86intrin.h> #endif #ifdef __F16C__ #define INTRIN_F16C 1 #include <x86intrin.h> #endif #ifdef __FMA__ #define INTRIN_FMA 1 #include <x86intrin.h> #endif #ifdef __FMA4__ #define INTRIN_FMA4 1 #include <x86intrin.h> #endif #ifdef __XOP__ #define INTRIN_XOP 1 #include <xopintrin.h> #endif #ifdef __LWP__ #define INTRIN_LWP 1 #include <x86intrin.h> #endif #ifdef __RDRND__ #define INTRIN_RDRND 1 #include <x86intrin.h> #endif #ifdef __FSGSBASE__ #define INTRIN_FSGSBASE 1 #include <x86intrin.h> #endif #ifdef __POPCNT__ #define INTRIN_POPCNT 1 #include <popcntintrin.h> #endif #ifdef __LZCNT__ #define INTRIN_LZCNT 1 #include <x86intrin.h> #endif #ifdef __TBM__ #define INTRIN_TBM 1 #include <x86intrin.h> #endif #ifdef __BMI__ #define INTRIN_BMI 1 #include <x86intrin.h> #endif #ifdef __BMI2__ #define INTRIN_BMI2 1 #include <x86intrin.h> #endif #endif //#if (defined(__i386__) || defined(__x86_64__) ) #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_POPCNT 1 // nmmintrin.h #define INTRIN_SSE4A 1 // intrin.h #define INTRIN_LZCNT 1 // intrin.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 1 //TODO:待查證, 注釋掉. //#define INTRIN_FMA 1 //#define INTRIN_F16C 1 //#define INTRIN_RDRND 1 //#define INTRIN_FSGSBASE 1 //#define INTRIN_TBM 1 //#define INTRIN_BMI 1 //#define INTRIN_BMI2 1 #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
2.2 testzintrin.c
全部代碼——

#define __STDC_LIMIT_MACROS 1 // C99整數范圍常量. [僅演示, 純C程序可以不用, 而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_POPCNT PT_MAKE_STR(INTRIN_POPCNT), #endif #ifdef INTRIN_LZCNT PT_MAKE_STR(INTRIN_LZCNT), #endif #ifdef INTRIN_TBM PT_MAKE_STR(INTRIN_TBM), #endif #ifdef INTRIN_BMI PT_MAKE_STR(INTRIN_BMI), #endif #ifdef INTRIN_BMI2 PT_MAKE_STR(INTRIN_BMI2), #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()); printf("testzintrin v1.01 (%dbit)\n\n", INTRIN_WORDSIZE); 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)); #if INTRIN_WORDSIZE>=64 printf("popcnt(0xffffffffffffffffull):\t%u\n", (int)_mm_popcnt_u64(0xffffffffffffffffull)); #endif #endif return 0; }
2.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版。
參考文獻——
《ISO/IEC 9899:1999 (C99)》。ISO/IEC,1999。www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
《Predefined Macros》. http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.110).aspx
《[GCC] The C Preprocessor》中的《3.7.2 Common Predefined Macros》. GNU, 2011. http://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html
《兼容C99標准的stdint.h》. http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.html
《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
《發現Mac OS X的llvm-gcc也是支持intrin函數的》. http://www.cnblogs.com/zyl910/archive/2012/09/27/intrin_mac.html
《[C] zintrin.h : 智能引入intrinsic函數。支持VC、GCC,兼容Windows、Linux、Mac OS X》. http://www.cnblogs.com/zyl910/archive/2012/09/23/zintrin.html