700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > [C] zintrin.h : 智能引入intrinsic函数。支持VC GCC 兼容Windows Linux Mac OS X

[C] zintrin.h : 智能引入intrinsic函数。支持VC GCC 兼容Windows Linux Mac OS X

时间:2019-11-10 14:03:57

相关推荐

[C] zintrin.h : 智能引入intrinsic函数。支持VC GCC 兼容Windows Linux Mac OS X

作者: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函数。

从VC开始,提供了intrin.h,用于引入所有的intrinsic函数。

详见——

/zyl910/archive//02/28/vs_intrin_table.html

Intrinsics头文件与SIMD指令集、Visual Studio版本对应表

如果希望得知当前编译环境是否支持某种intrinsic函数,只能利用_MSC_VER判断VC的版本 来间接确认。

而且实际过程中会发现一些兼容性小问题,例如——VC不支持x64下的MMX指令、VC之前没有_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”子目录中。

详见——

/zyl910/archive//08/27/intrin_table_gcc.html

GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表

gcc允许通过命令行参数来控制是否打开某种指令集的支持,例如“-mmmx”用于打开MMX支持。可在终端中执行“gcc --target-help”,得到详细的参数列表。

当通过命令行参数打开指令集支持后,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函数的办法——

对于VS之前的版本,只能手动逐个逐个的包含emmintrin.h、mm3dnow.h;

对于VS之后的版本,可以利用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_MMX1#endif#ifdef __3dNOW__#define INTRIN_3dNOW1#endif#ifdef __SSE__#define INTRIN_SSE1#endif#ifdef __SSE2__#define INTRIN_SSE21#endif#ifdef __SSE3__#define INTRIN_SSE31#endif#ifdef __SSSE3__#define INTRIN_SSSE31#endif#ifdef __SSE4_1__#define INTRIN_SSE4_11#endif#ifdef __SSE4_2__#define INTRIN_SSE4_21#endif#ifdef __SSE4A__#define INTRIN_SSE4A1#endif#ifdef __AES__#define INTRIN_AES1#endif#ifdef __PCLMUL__#define INTRIN_PCLMUL1#endif#ifdef __AVX__#define INTRIN_AVX1#endif#ifdef __AVX2__#define INTRIN_AVX21#endif#ifdef __F16C__#define INTRIN_F16C1#endif#ifdef __FMA__#define INTRIN_FMA1#endif#ifdef __FMA4__#define INTRIN_FMA41#endif#ifdef __XOP__#define INTRIN_XOP1#endif#ifdef __LWP__#define INTRIN_LWP1#endif#ifdef __RDRND__#define INTRIN_RDRND1#endif#ifdef __FSGSBASE__#define INTRIN_FSGSBASE1#endif#ifdef __LZCNT__#define INTRIN_LZCNT1#endif#ifdef __POPCNT__#define INTRIN_POPCNT1#endif#ifdef __BMI__#define INTRIN_BMI1#endif#ifdef __BMI2__#define INTRIN_BMI21#endif#ifdef __TBM__#define INTRIN_TBM1#endif#endif// #if !defined(__llvm__)#elif defined(_MSC_VER)// MSVC// header files#if _MSC_VER >=1400// VC#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_MMX1// mmintrin.h#define INTRIN_3dNOW1// mm3dnow.h#endif#define INTRIN_SSE1// xmmintrin.h#define INTRIN_SSE21// emmintrin.h#endif#if _MSC_VER >=1300// VC#endif#if _MSC_VER >=1400// VC#endif#if _MSC_VER >=1500// VC#define INTRIN_SSE31// pmmintrin.h#define INTRIN_SSSE31// tmmintrin.h#define INTRIN_SSE4_11// smmintrin.h#define INTRIN_SSE4_21// nmmintrin.h#define INTRIN_SSE4A1// intrin.h#define INTRIN_LZCNT1// intrin.h#define INTRIN_POPCNT1// nmmintrin.h#endif#if _MSC_VER >=1600// VC#define INTRIN_AES1// wmmintrin.h#define INTRIN_PCLMUL1// wmmintrin.h#define INTRIN_AVX1// immintrin.h#define INTRIN_FMA41// ammintrin.h#define INTRIN_XOP1// ammintrin.h#define INTRIN_LWP1// ammintrin.h#endif#if _MSC_VER >=1700// VC#define INTRIN_AVX20//TODO:待查证. 先设为0.#define INTRIN_FMA0#define INTRIN_F16C0#define INTRIN_RDRND0#define INTRIN_FSGSBASE0#define INTRIN_BMI0#define INTRIN_BMI20#define INTRIN_TBM0#endif#endif//TODO:待查证 VS配合intel C编译器时intrin函数的支持性.// VC之前没有_mm_cvtss_f32#if _MSC_VER <1500// VC// 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)#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_LZCNTPT_MAKE_STR(INTRIN_LZCNT),#endif#ifdef INTRIN_POPCNTPT_MAKE_STR(INTRIN_POPCNT),#endif#ifdef INTRIN_BMIPT_MAKE_STR(INTRIN_BMI),#endif#ifdef INTRIN_BMI2PT_MAKE_STR(INTRIN_BMI2),#endif#ifdef INTRIN_TBMPT_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_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));#endifreturn 0;}

3.3 makefile

全部代码——

# flagsCC = gccCFS = -WallLFS = # argsRELEASE =0BITS =CFLAGS = -msse# [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.ifeq ($(RELEASE),0)# debugCFS += -gelse# releaseCFS += -static -O3 -DNDEBUGLFS += -staticendif# [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32.ifeq ($(BITS),32)CFS += -m32LFS += -m32elseifeq ($(BITS),64)CFS += -m64LFS += -m64elseendifendif# [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 $<clean :rm -f $(OBJS) $(TARGETS) $(addsuffix .exe,$(TARGETS))

四、测试

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

VC6:x86版。

VC:x86版。

VC:x86版、x64版。

VC:x86版、x64版。

GCC 4.7.0(Fedora 17 x64):x86版、x64版。

GCC 4.6.2(MinGW(0426)):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》. /en-us/library/b0084kay(v=vs.110).aspx

《Intrinsics头文件与SIMD指令集、Visual Studio版本对应表》. /zyl910/archive//02/28/vs_intrin_table.html

《GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表》. /zyl910/archive//08/27/intrin_table_gcc.html

源码下载——

/zyl910/zintrin.rar

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。