博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[C] zintrin.h: 智能引入intrinsic函数 V1.02版。支持VC2012,增加INTRIN_ALIGN、INTRIN_COMPILER_NAME宏...
阅读量:6243 次
发布时间:2019-06-22

本文共 14996 字,大约阅读时间需要 49 分钟。

作者:。

一、更新说明

1.1 支持VC2012

  VC2012增加了很多Intrinsics函数,详见——

检查了一下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_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#ifdef INTRIN_INVPCID PT_MAKE_STR(INTRIN_INVPCID),#endif#ifdef INTRIN_HLE PT_MAKE_STR(INTRIN_HLE),#endif#ifdef INTRIN_RTM PT_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_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 // avx2: only Haswell+#ifdef INTRIN_AVX2 if(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] VXORPS ymm1 = _mm256_add_epi16(ymm1, ymm1); // // [AVX2] VPADDW _mm256_store_si256((__m256i*)buf, ymm1); // [AVX] VMOVDQA printf("ymm1[0]:\t%u\n", buf[0]); }#endif return 0;}

 

 

2.3 makefile

  全部代码——

View Code
# flagsCC = gccCFS = -WallLFS = # argsRELEASE =0BITS =CFLAGS = -msse# [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.ifeq ($(RELEASE),0)    # debug    CFS += -gelse    # release    CFS += -static -O3 -DNDEBUG    LFS += -staticendif# [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32.ifeq ($(BITS),32)    CFS += -m32    LFS += -m32else    ifeq ($(BITS),64)        CFS += -m64        LFS += -m64    else    endifendif# [args] 使用 CFLAGS 添加新的参数. make CFLAGS="-mpopcnt -msse4a".CFS += $(CFLAGS).PHONY : all clean# filesTARGETS = testzintrinOBJS = testzintrin.oall : $(TARGETS)testzintrin : $(OBJS)    $(CC) $(LFS) -o $@ $^testzintrin.o : testzintrin.c zintrin.h    $(CC) $(CFS) -c $

 

三、测试

  在以下编译器中成功编译——

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] VXORPS  000af    c5 fc 57 c0     vxorps     ymm0, ymm0, ymm0  000b3    c5 fd 7f 44 24    3c         vmovdqa YMMWORD PTR _ymm1$2[esp+92], ymm0; 236  :         ymm1 = _mm256_add_epi16(ymm1, ymm1);    // // [AVX2] VPADDW  000b9    c5 fd 6f 4c 24    3c         vmovdqa ymm1, YMMWORD PTR _ymm1$2[esp+92]  000bf    c5 fd 6f 44 24    3c         vmovdqa ymm0, YMMWORD PTR _ymm1$2[esp+92]  000c5    c5 fd fd c1     vpaddw     ymm0, ymm0, ymm1  000c9    c5 fd 7f 44 24    3c         vmovdqa YMMWORD PTR _ymm1$2[esp+92], ymm0; 237  :         _mm256_store_si256((__m256i*)buf, ymm1);    // [AVX] VMOVDQA  000cf    c5 fd 6f 44 24    3c         vmovdqa ymm0, YMMWORD PTR _ymm1$2[esp+92]

 

 

参考文献——

《ISO/IEC 9899:1999 (C99)》。ISO/IEC,1999。
《Predefined Macros》.
《[GCC] The C Preprocessor》中的《3.7.2 Common Predefined Macros》. GNU, 2011.
《兼容C99标准的stdint.h》.
《Intrinsics头文件与SIMD指令集、Visual Studio版本对应表》.
《GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表》.
《检查了一下VC2012新增的Intrinsics函数集,发现它支持ARM指令和Haswell新指令》.
《[C] zintrin.h : 智能引入intrinsic函数。支持VC、GCC,兼容Windows、Linux、Mac OS X》.
《[C] zintrin.h: 智能引入intrinsic函数 V1.01版。改进对Mac OS X的支持,增加INTRIN_WORDSIZE宏》.

 

源码下载——

转载地址:http://ensia.baihongyu.com/

你可能感兴趣的文章
数据仓库与数据挖掘的一些基本概念
查看>>
Android学习系列(23)--App主界面实现
查看>>
jquery validate的漂亮css样式验证
查看>>
OAF_解决OAF与Windows版本不兼容黑屏
查看>>
如何让编码更加的标准
查看>>
阿里云收集服务器性能指标的python脚本
查看>>
Docker源码分析(一):Docker架构
查看>>
Android开发之在子线程中使用Toast
查看>>
(第三天)函数
查看>>
Git 学习笔记--Git下的冲突解决
查看>>
poj 2955 Brackets(区间dp)
查看>>
jQuery选中该复选框来实现/全部取消/未选定/获得的选定值
查看>>
武汉Uber优步司机奖励政策(8月31日~9月6日)
查看>>
javascript小技巧:同步服务器时间、同步倒计时
查看>>
JUnit4.8.2来源分析-2 org.junit.runner.Request
查看>>
你觉得你在创业,但其实你可能只是在做小生意而已 制定正确的计划 创业和经营小企业之间的差异...
查看>>
HDU 4847-Wow! Such Doge!(定位)
查看>>
冒泡排序算法 C++和PHP达到
查看>>
Android 弹出通知Toast的使用
查看>>
jquery $.each遍历json数组方法
查看>>