设为首页 收藏本站
查看: 1228|回复: 0

[经验分享] [C] zintrin.h: 智能引入intrinsic函数 V1.01版。改进对Mac OS X的支持,增加INTRIN_WORDSIZE宏

[复制链接]

尚未签到

发表于 2015-12-29 15:53:20 | 显示全部楼层 |阅读模式
  新版本——
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
  全部代码——


DSC0000.gif DSC0001.gif 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_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

  全部代码——


View Code


#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.name, pArray.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

  全部代码——


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)
# 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版。
DSC0002.png
DSC0003.png
  
  参考文献——
《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
  
  源码下载——
http://files.cnblogs.com/zyl910/zintrin_v101.rar

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-158099-1-1.html 上篇帖子: Mac OS X 上Lua的安装方法 下篇帖子: Mac OS X下的symbol追踪!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表