[C] zintrin.h: 智能引入intrinsic函数 V1.02版。支持VC2012,增加INTRIN_ALIGN、INTRIN_COMPILER_NAME宏...

作者:zyl910。

一、更新说明

1.1 支持VC2012

  VC2012增加了很多Intrinsics函数,详见——
http://www.cnblogs.com/zyl910/archive/2012/11/06/intrin_vc2012.html
检查了一下VC2012新增的Intrinsics函数集,发现它支持ARM指令和Haswell新指令

  根据上述信息改进zintrin,检测到是VC2012时定义相关的宏——

        #if _MSC_VER >=1700    // VC2012#define INTRIN_AVX2    1    // immintrin.h#define INTRIN_FMA    1    // immintrin.h#define INTRIN_F16C    1    // immintrin.h#define INTRIN_RDRND    1    // immintrin.h#define INTRIN_FSGSBASE    1    // immintrin.h#define INTRIN_TBM    1    // ammintrin.h#define INTRIN_LZCNT    1    // immintrin.h#define INTRIN_BMI    1    // immintrin.h#define INTRIN_BMI2    1    // immintrin.h#define INTRIN_INVPCID    1    // immintrin.h#define INTRIN_HLE    1    // immintrin.h#define INTRIN_RTM    1    // immintrin.h#endif

 

  INVPCID、HLE、RTM是Haswell的新指令,原先的“INTRIN_*”判断宏没有它们。于是增加了这几个定义——
INTRIN_INVPCID
INTRIN_HLE
INTRIN_RTM


  通过对比VC2010与VC2012的头文件后发现——虽然以前intrin.h支持lzcnt,但它是双下划线版的,而不是Intel所规定的单下划线版。直到VC2012,才支持单下划线版lzcnt。
  具体修正做法——注释掉以前的INTRIN_LZCNT。检测到是VC2012时,才定义INTRIN_LZCNT。


1.2 增加INTRIN_ALIGN宏(地址对齐)

  在使用SIMD指令时,经常需要地址对齐。例如 MMX要求8字节对齐,SSE要求16字节对齐,AVX要求32字节对齐。
  因C语言标准未考虑该问题,VC与GCC的实现方法都不同。于是我定义一个INTRIN_ALIGN宏,用来统一处理地址对齐问题——

#if defined(__GNUC__)    // GCC#define INTRIN_ALIGN(n)    __attribute__((aligned(n)))
#else    // 否则使用VC格式.#define INTRIN_ALIGN(n)    __declspec(align(n))
#endif    // #if defined(__GNUC__)    // GCC

 

  使用时,将该宏写在数据类型的前面。例如——
INTRIN_ALIGN(32) uint_16 buf[16];


1.3 增加INTRIN_COMPILER_NAME宏(编译器名称)

  在使用Intrinsics函数时,经常需要得知编译器名称,以方便进行对比分析。于是我定义了INTRIN_COMPILER_NAME宏——

#define INTRIN_MACTOSTR(x)    #x
#define INTRIN_MACROVALUESTR(x)    INTRIN_MACTOSTR(x)
#if defined(__ICL)    // Intel C++
#  if defined(__VERSION__)
#    define INTRIN_COMPILER_NAME    "Intel C++ " __VERSION__
#  elif defined(__INTEL_COMPILER_BUILD_DATE)
#    define INTRIN_COMPILER_NAME    "Intel C++ (" INTRIN_MACROVALUESTR(__INTEL_COMPILER_BUILD_DATE) ")"
#  else
#    define INTRIN_COMPILER_NAME    "Intel C++"
#  endif    // #  if defined(__VERSION__)
#elif defined(_MSC_VER)    // Microsoft VC++
#  if defined(_MSC_FULL_VER)
#    define INTRIN_COMPILER_NAME    "Microsoft VC++ (" INTRIN_MACROVALUESTR(_MSC_FULL_VER) ")"
#  elif defined(_MSC_VER)
#    define INTRIN_COMPILER_NAME    "Microsoft VC++ (" INTRIN_MACROVALUESTR(_MSC_VER) ")"
#  else
#    define INTRIN_COMPILER_NAME    "Microsoft VC++"
#  endif    // #  if defined(_MSC_FULL_VER)
#elif defined(__GNUC__)    // GCC
#  if defined(__CYGWIN__)
#    define INTRIN_COMPILER_NAME    "GCC(Cygmin) " __VERSION__
#  elif defined(__MINGW32__)
#    define INTRIN_COMPILER_NAME    "GCC(MinGW) " __VERSION__
#  else
#    define INTRIN_COMPILER_NAME    "GCC " __VERSION__
#  endif    // #  if defined(_MSC_FULL_VER)
#else
#  define INTRIN_COMPILER_NAME    "Unknown Compiler"
#endif    // #if defined(__ICL)    // Intel C++

 

  示例——
printf("Compiler: %s\n", INTRIN_COMPILER_NAME);

二、全部代码

2.1 zintrin.h

  全部代码——

View Code
#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_ALIGN(x): 变量地址对齐.
#if defined(__GNUC__)    // GCC#define INTRIN_ALIGN(n)    __attribute__((aligned(n)))
#else    // 否则使用VC格式.#define INTRIN_ALIGN(n)    __declspec(align(n))
#endif    // #if defined(__GNUC__)    // GCC// INTRIN_COMPILER_NAME: 编译器名称.
#define INTRIN_MACTOSTR(x)    #x
#define INTRIN_MACROVALUESTR(x)    INTRIN_MACTOSTR(x)
#if defined(__ICL)    // Intel C++
#  if defined(__VERSION__)
#    define INTRIN_COMPILER_NAME    "Intel C++ " __VERSION__
#  elif defined(__INTEL_COMPILER_BUILD_DATE)
#    define INTRIN_COMPILER_NAME    "Intel C++ (" INTRIN_MACROVALUESTR(__INTEL_COMPILER_BUILD_DATE) ")"
#  else
#    define INTRIN_COMPILER_NAME    "Intel C++"
#  endif    // #  if defined(__VERSION__)
#elif defined(_MSC_VER)    // Microsoft VC++
#  if defined(_MSC_FULL_VER)
#    define INTRIN_COMPILER_NAME    "Microsoft VC++ (" INTRIN_MACROVALUESTR(_MSC_FULL_VER) ")"
#  elif defined(_MSC_VER)
#    define INTRIN_COMPILER_NAME    "Microsoft VC++ (" INTRIN_MACROVALUESTR(_MSC_VER) ")"
#  else
#    define INTRIN_COMPILER_NAME    "Microsoft VC++"
#  endif    // #  if defined(_MSC_FULL_VER)
#elif defined(__GNUC__)    // GCC
#  if defined(__CYGWIN__)
#    define INTRIN_COMPILER_NAME    "GCC(Cygmin) " __VERSION__
#  elif defined(__MINGW32__)
#    define INTRIN_COMPILER_NAME    "GCC(MinGW) " __VERSION__
#  else
#    define INTRIN_COMPILER_NAME    "GCC " __VERSION__
#  endif    // #  if defined(_MSC_FULL_VER)
#else
#  define INTRIN_COMPILER_NAME    "Unknown Compiler"
#endif    // #if defined(__ICL)    // Intel C++// 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     // mac下有时找不到. 于是放弃, 使用ccpuid模块会更方便.//#include     // mac下有时找不到. 于是根据宏来加载头文件.// macros
        #ifdef __MMX__#define INTRIN_MMX    1#include #endif#ifdef __3dNOW__#define INTRIN_3dNOW    1#include #endif#ifdef __SSE__#define INTRIN_SSE    1#include #endif#ifdef __SSE2__#define INTRIN_SSE2    1#include #endif#ifdef __SSE3__#define INTRIN_SSE3    1#include #endif#ifdef __SSSE3__#define INTRIN_SSSE3    1#include #endif#ifdef __SSE4_1__#define INTRIN_SSE4_1    1#include #endif#ifdef __SSE4_2__#define INTRIN_SSE4_2    1#include #endif#ifdef __SSE4A__#define INTRIN_SSE4A    1#include #endif#ifdef __AES__#define INTRIN_AES    1#include #endif#ifdef __PCLMUL__#define INTRIN_PCLMUL    1#include #endif#ifdef __AVX__#define INTRIN_AVX    1#include #endif#ifdef __AVX2__#define INTRIN_AVX2    1#include #endif#ifdef __F16C__#define INTRIN_F16C    1#include #endif#ifdef __FMA__#define INTRIN_FMA    1#include #endif#ifdef __FMA4__#define INTRIN_FMA4    1#include #endif#ifdef __XOP__#define INTRIN_XOP    1#include #endif#ifdef __LWP__#define INTRIN_LWP    1#include #endif#ifdef __RDRND__#define INTRIN_RDRND    1#include #endif#ifdef __FSGSBASE__#define INTRIN_FSGSBASE    1#include #endif#ifdef __POPCNT__#define INTRIN_POPCNT    1#include #endif#ifdef __LZCNT__#define INTRIN_LZCNT    1#include #endif#ifdef __TBM__#define INTRIN_TBM    1#include #endif#ifdef __BMI__#define INTRIN_BMI    1#include #endif#ifdef __BMI2__#define INTRIN_BMI2    1#include #endif#endif    //#if (defined(__i386__) || defined(__x86_64__) )#elif defined(_MSC_VER)    // MSVC// header files#if _MSC_VER >=1400    // VC2005#include #endif    // #if _MSC_VER >=1400#if (defined(_M_IX86) || defined(_M_X64))// header files#if _MSC_VER >=1200    // VC6#include     // MMX, SSE, SSE2#include     // 3DNow!#endif    // #if _MSC_VER >=1400#include     // _mm_malloc, _mm_free.// macros#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    // immintrin.h#define INTRIN_FMA    1    // immintrin.h#define INTRIN_F16C    1    // immintrin.h#define INTRIN_RDRND    1    // immintrin.h#define INTRIN_FSGSBASE    1    // immintrin.h#define INTRIN_TBM    1    // ammintrin.h#define INTRIN_LZCNT    1    // immintrin.h#define INTRIN_BMI    1    // immintrin.h#define INTRIN_BMI2    1    // immintrin.h#define INTRIN_INVPCID    1    // immintrin.h#define INTRIN_HLE    1    // immintrin.h#define INTRIN_RTM    1    // immintrin.h#endif// 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#elif defined(_M_ARM)// VC2012增加了 armintr.h、arm_neon.h。其中armintr.h会被intrin.h自动引入.#endif    // #if (defined(_M_IX86) || defined(_M_X64))//TODO:待查证 VS配合intel C编译器时intrin函数的支持性.#else
//#error Only supports GCC or MSVC.
#endif    // #if defined(__GNUC__)#endif    // #ifndef __ZINTRIN_H_INCLUDED

 

 


2.2 testzintrin.c

  全部代码——

View Code
#define __STDC_LIMIT_MACROS    1    // C99整数范围常量. [仅演示, 纯C程序可以不用, 而C++程序必须定义该宏.]#include #include "zintrin.h"#define PT_MAKE_STR(x)    { #x, PT_MAKE_STR_ESC(x) }
#define PT_MAKE_STR_ESC(x)    #xtypedef struct tagMACRO_T
{const char *name;const char *value;
} MACRO_T;/* Intrinsics */
const MACRO_T g_intrins[] =
{{"[Intrinsics]", ""},#ifdef INTRIN_MMXPT_MAKE_STR(INTRIN_MMX),
#endif#ifdef INTRIN_3dNOWPT_MAKE_STR(INTRIN_3dNOW),
#endif#ifdef INTRIN_SSEPT_MAKE_STR(INTRIN_SSE),
#endif#ifdef INTRIN_SSE2PT_MAKE_STR(INTRIN_SSE2),
#endif#ifdef INTRIN_SSE3PT_MAKE_STR(INTRIN_SSE3),
#endif#ifdef INTRIN_SSSE3PT_MAKE_STR(INTRIN_SSSE3),
#endif#ifdef INTRIN_SSE4_1PT_MAKE_STR(INTRIN_SSE4_1),
#endif#ifdef INTRIN_SSE4_2PT_MAKE_STR(INTRIN_SSE4_2),
#endif#ifdef INTRIN_SSE4APT_MAKE_STR(INTRIN_SSE4A),
#endif#ifdef INTRIN_AESPT_MAKE_STR(INTRIN_AES),
#endif#ifdef INTRIN_PCLMULPT_MAKE_STR(INTRIN_PCLMUL),
#endif#ifdef INTRIN_AVXPT_MAKE_STR(INTRIN_AVX),
#endif#ifdef INTRIN_AVX2PT_MAKE_STR(INTRIN_AVX2),
#endif#ifdef INTRIN_F16CPT_MAKE_STR(INTRIN_F16C),
#endif#ifdef INTRIN_FMAPT_MAKE_STR(INTRIN_FMA),
#endif#ifdef INTRIN_FMA4PT_MAKE_STR(INTRIN_FMA4),
#endif#ifdef INTRIN_XOPPT_MAKE_STR(INTRIN_XOP),
#endif#ifdef INTRIN_LWPPT_MAKE_STR(INTRIN_LWP),
#endif#ifdef INTRIN_RDRNDPT_MAKE_STR(INTRIN_RDRND),
#endif#ifdef INTRIN_FSGSBASEPT_MAKE_STR(INTRIN_FSGSBASE),
#endif#ifdef INTRIN_POPCNTPT_MAKE_STR(INTRIN_POPCNT),
#endif#ifdef INTRIN_LZCNTPT_MAKE_STR(INTRIN_LZCNT),
#endif#ifdef INTRIN_TBMPT_MAKE_STR(INTRIN_TBM),
#endif#ifdef INTRIN_BMIPT_MAKE_STR(INTRIN_BMI),
#endif#ifdef INTRIN_BMI2PT_MAKE_STR(INTRIN_BMI2),
#endif#ifdef INTRIN_INVPCIDPT_MAKE_STR(INTRIN_INVPCID),
#endif#ifdef INTRIN_HLEPT_MAKE_STR(INTRIN_HLE),
#endif#ifdef INTRIN_RTMPT_MAKE_STR(INTRIN_RTM),
#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.02 (%dbit)\n\n", INTRIN_WORDSIZE);printf("Compiler: %s\n", INTRIN_COMPILER_NAME);print_MACRO_T(g_intrins, sizeof(g_intrins)/sizeof(g_intrins[0]));// _mm_malloc
#ifdef INTRIN_SSEif(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_SSEif(1){__m128 xmm1;float f;printf("&xmm1:\t%ph\n", &xmm1);xmm1 = _mm_setzero_ps();    // SSE instruction: xorpsf = _mm_cvtss_f32(xmm1);printf("_mm_cvtss_f32:\t%f\n", f);}
#endif// popcnt
#ifdef INTRIN_POPCNTprintf("popcnt(0xffffffffu):\t%u\n", _mm_popcnt_u32(0xffffffffu));#if INTRIN_WORDSIZE>=64printf("popcnt(0xffffffffffffffffull):\t%u\n", (int)_mm_popcnt_u64(0xffffffffffffffffull));#endif
#endif// avx2: only Haswell+
#ifdef INTRIN_AVX2if(1){INTRIN_ALIGN(32) uint16_t buf[16];volatile __m256i ymm1;    // volatile是为了避免编译优化剔除下面的AVX2代码.printf("&ymm1:\t%ph\n", &ymm1);ymm1 = _mm256_setzero_si256();    // [AVX2] VPXOR. 编译优化时可能会变为 [AVX] VXORPSymm1 = _mm256_add_epi16(ymm1, ymm1);    // // [AVX2] VPADDW_mm256_store_si256((__m256i*)buf, ymm1);    // [AVX] VMOVDQAprintf("ymm1[0]:\t%u\n", buf[0]);}
#endifreturn 0;
}

 

 


2.3 makefile

  全部代码——

View Code
# flags
CC = gcc
CFS = -Wall
LFS = # args
RELEASE =0
BITS =
CFLAGS = -msse# [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.
ifeq ($(RELEASE),0)# debugCFS += -g
else# releaseCFS += -static -O3 -DNDEBUGLFS += -static
endif# [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32.
ifeq ($(BITS),32)CFS += -m32LFS += -m32
elseifeq ($(BITS),64)CFS += -m64LFS += -m64elseendif
endif# [args] 使用 CFLAGS 添加新的参数. make CFLAGS="-mpopcnt -msse4a".
CFS += $(CFLAGS).PHONY : all clean# files
TARGETS = testzintrin
OBJS = testzintrin.oall : $(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版。
VC2012: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版。

  运行效果——

  出现“停止工作”对话框是正常的。表示编译器已经正常的生成了AVX2代码,但因CPU不支持AVX2指令而抛出异常(2013的Haswell才支持AVX2)。
  可检查汇编列表文件验证这一论断。摘自“Release\testzintrin.cod”——

; 235  :         ymm1 = _mm256_setzero_si256();    // [AVX2] VPXOR. 编译优化时可能会变为 [AVX] VXORPS000af    c5 fc 57 c0     vxorps     ymm0, ymm0, ymm0000b3    c5 fd 7f 44 243c         vmovdqa YMMWORD PTR _ymm1$2[esp+92], ymm0; 236  :         ymm1 = _mm256_add_epi16(ymm1, ymm1);    // // [AVX2] VPADDW000b9    c5 fd 6f 4c 243c         vmovdqa ymm1, YMMWORD PTR _ymm1$2[esp+92]000bf    c5 fd 6f 44 243c         vmovdqa ymm0, YMMWORD PTR _ymm1$2[esp+92]000c5    c5 fd fd c1     vpaddw     ymm0, ymm0, ymm1000c9    c5 fd 7f 44 243c         vmovdqa YMMWORD PTR _ymm1$2[esp+92], ymm0; 237  :         _mm256_store_si256((__m256i*)buf, ymm1);    // [AVX] VMOVDQA000cf    c5 fd 6f 44 243c         vmovdqa ymm0, YMMWORD PTR _ymm1$2[esp+92]

 

 

参考文献——
《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
《检查了一下VC2012新增的Intrinsics函数集,发现它支持ARM指令和Haswell新指令》. http://www.cnblogs.com/zyl910/archive/2012/11/06/intrin_vc2012.html
《[C] zintrin.h : 智能引入intrinsic函数。支持VC、GCC,兼容Windows、Linux、Mac OS X》. http://www.cnblogs.com/zyl910/archive/2012/09/23/zintrin.html
《[C] zintrin.h: 智能引入intrinsic函数 V1.01版。改进对Mac OS X的支持,增加INTRIN_WORDSIZE宏》. http://www.cnblogs.com/zyl910/archive/2012/10/01/zintrin_v101.html

 

源码下载——
http://files.cnblogs.com/zyl910/zintrin_v102.rar


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部